A running instance takes one docker run command and the first start that configures it. The instance answers over HTTP first; TLS is the last step of this page.
Before you start
Check the host against Requirements and pick <HOSTNAME>: the name users open in the browser and put into Git remotes. The same name goes into the address of the instance and into the certificate.
Check that the name resolves to the address of the host:
getent hosts <HOSTNAME>Without a DNS record, add the name to /etc/hosts on the host and on every machine that opens the web interface or works with Git.
Getting the image
The image is published per flavor: a flavor names the base distribution and stands for <FLAVOR> in the image reference.
| Flavor | Base distribution |
|---|---|
ubuntu_22.04 |
Ubuntu 22.04 |
ubuntu_24.04 |
Ubuntu 24.04 |
redos_8 |
RED OS 8 |
Every tag names a version, and no tag follows the latest one, so both the first run and every upgrade name the version explicitly:
docker pull <REGISTRY>/<FLAVOR>:<VERSION>Starting the container
Create the container with the volumes, the published ports and the address of the instance:
docker run -d --name code \
--shm-size 256m \
-p 80:80 -p 443:443 -p 22:22 \
-e GITLAB_OMNIBUS_CONFIG="external_url 'http://<HOSTNAME>'" \
-v /srv/code/config:/etc/gitlab \
-v /srv/code/logs:/var/log/gitlab \
-v /srv/code/data:/var/opt/gitlab \
<REGISTRY>/<FLAVOR>:<VERSION>The name code is used by every command below. The left-hand port of each -p is the host port and may differ from the container port; port 22 on the host is usually taken by the host sshd, and Post-installation setup describes the setting that names another port in clone URLs.
Ports, volumes and environment variables are fixed when the container is created. Changing any of them means removing the container and creating a new one with the same volumes.
Address of the instance
Without configuration the address is http://<CONTAINER_HOSTNAME>, and Docker sets the container host name to the container identifier unless --hostname is given. That identifier would then appear in every link and clone URL, so set the address on the first run.
The address is the external_url setting of gitlab.rb, and the image takes it from the environment variable and from the configuration file:
GITLAB_OMNIBUS_CONFIGholds configuration lines and is evaluated on every start;/etc/gitlab/gitlab.rbon the configuration volume is read after that, so a setting written into the file overrides the same setting in the variable.
EXTERNAL_URL, the variable the package installer reads, has no effect in the container.
First start
The start-up script prepares the instance before the services accept requests:
- copies
gitlab.rbfrom the template when the configuration volume has no such file; - generates the SSH host keys into
/etc/gitlabwhen they are absent; - runs
gitlab-ctl reconfigure, the step that takes several minutes on the first start; - brings the database to the current PostgreSQL version;
- tails the service logs into the container output.
Follow the start in the container output:
docker logs -f codeThe image carries a health check that runs every 60 seconds. Until the first successful check the container health is starting, and five failed checks in a row turn it into unhealthy, which the first start reaches while the configuration is still running:
docker inspect --format '{{.State.Health.Status}}' codeThe start has finished when the health is healthy and every service is in state run:
docker exec code gitlab-ctl statusFirst sign-in
The first root password is written to the configuration volume. Read it from the container:
docker exec code cat /etc/gitlab/initial_root_passwordOpen http://<HOSTNAME> and sign in as root with that password.
The password file is deleted by the first configuration run that happens more than 24 hours after the file was written, and the TLS step below is such a run. Change the root password in the web interface and delete the file: docker exec code rm -f /etc/gitlab/initial_root_password.
HTTP carries the sign-in data and the repository content unencrypted. An instance that serves users runs over TLS.
TLS
Put the certificate and the key on the configuration volume, so that the container reads them from /etc/gitlab/ssl:
sudo mkdir -p /srv/code/config/ssl && sudo chmod 755 /srv/code/config/ssl
sudo install -m 644 <CERT_FILE> /srv/code/config/ssl/<HOSTNAME>.crt
sudo install -m 600 <KEY_FILE> /srv/code/config/ssl/<HOSTNAME>.key<CERT_FILE> is the certificate file in PEM format, <KEY_FILE> is its private key file.
Issuing the certificate, the requirement for a subjectAltName and the reason Let’s Encrypt is turned off are described in Quick start.
Add the settings to /srv/code/config/gitlab.rb, which the container reads as /etc/gitlab/gitlab.rb:
external_url 'https://<HOSTNAME>'
letsencrypt['enable'] = false
nginx['redirect_http_to_https'] = true
nginx['ssl_certificate'] = "/etc/gitlab/ssl/<HOSTNAME>.crt"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/<HOSTNAME>.key"The paths in the settings are container paths. Apply the change by restarting the container, because every start runs the configuration:
docker restart codeVerification
Check the services, the health check and the redirect from HTTP:
docker exec code gitlab-ctl status
docker exec code gitlab-healthcheck --fail --max-time 10
docker inspect --format '{{.State.Health.Status}}' code
curl -sI http://<HOSTNAME>/ | grep -i locationOpen https://<HOSTNAME> in the browser, sign in and create a test project to check Git access over HTTPS and SSH.