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_CONFIG holds configuration lines and is evaluated on every start;
  • /etc/gitlab/gitlab.rb on 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.rb from the template when the configuration volume has no such file;
  • generates the SSH host keys into /etc/gitlab when 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 code

The 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}}' code

The start has finished when the health is healthy and every service is in state run:

docker exec code gitlab-ctl status

First 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_password

Open 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 code

Verification

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 location

Open https://<HOSTNAME> in the browser, sign in and create a test project to check Git access over HTTPS and SSH.

Additional resources