Deckhouse Code runs on GitLab and takes over the data of a GitLab Community Edition (CE) instance of the same GitLab version: repositories, users, groups, issues, merge requests, CI/CD settings and history. The instance is moved as a whole. To move single projects between instances, use Importing a project.

The procedure depends on where Deckhouse Code runs:

  • on the same server or with the same Docker volumes as GitLab CE: the Deckhouse Code package or image replaces GitLab CE, and the data stays in place, as described in In-place migration;
  • on another server, in a Helm Chart installation or in the Deckhouse Kubernetes Platform module: the data is moved in a backup archive, as described in Migration through a backup archive.

Before you start

Check the conditions on the GitLab CE instance:

  • The GitLab version of the instance is the GitLab version of the Deckhouse Code release you install. A backup archive is restored only into the version it was created on. When the instance runs an older version, upgrade it along the GitLab upgrade path first.
  • All background migrations have finished: open “Admin” → “Monitoring” → “Background migrations” and check that the “Queued”, “Finalizing” and “Failed” tabs are empty.
  • There is enough free disk space for a backup archive.

Check the GitLab version of the instance:

  • Linux package
  • Omnibus Docker
sudo cat /opt/gitlab/embedded/service/gitlab-rails/VERSION

In the commands of this page, the GitLab CE container is named gitlab; replace the name with your own.

docker exec gitlab cat /opt/gitlab/embedded/service/gitlab-rails/VERSION

In-place migration

The Deckhouse Code package replaces the gitlab-ce package, and the Deckhouse Code image runs on the volumes of the GitLab CE container. The configuration in /etc/gitlab/gitlab.rb, the secrets in /etc/gitlab/gitlab-secrets.json and the data in /var/opt/gitlab are kept. The configuration run applies the database migrations that create the tables of Deckhouse Code.

The services restart during the installation, and the instance is unavailable until the configuration and the migrations finish.

  • Linux package
  • Omnibus Docker
  1. Create a backup and copy it, together with /etc/gitlab/gitlab.rb, /etc/gitlab/gitlab-secrets.json and the /etc/gitlab/ssl/ directory, off the server:

    sudo gitlab-backup create
  2. Install the Deckhouse Code package. The package manager removes the gitlab-ce package:

    # RED OS.
    sudo dnf install -y ./deckhouse-code-<VERSION>.el8.x86_64.rpm
    # AlmaLinux.
    sudo dnf install -y ./deckhouse-code-<VERSION>.el9.x86_64.rpm
    # Ubuntu.
    sudo apt install -y ./deckhouse-code_<VERSION>_amd64.deb
  3. Run the configuration:

    sudo gitlab-ctl reconfigure
  4. Check that no migration is pending, that the encrypted data in the database is read with the secrets of the instance and that the instance passes the self-check:

    sudo gitlab-rake db:migrate:status | grep -w down
    sudo gitlab-rake gitlab:fe:secret:verify
    sudo gitlab-rake gitlab:check SANITIZE=true

    The first command prints nothing when all migrations are applied. When it prints migrations, apply them and run the checks again:

    sudo gitlab-rake db:migrate

In the commands below, <GITLAB_HOME> is the host directory that holds the config, logs and data volumes of the GitLab CE container.

  1. Create a backup and copy it, together with the <GITLAB_HOME>/config directory, off the host:

    docker exec -t gitlab gitlab-backup create
  2. Stop and remove the GitLab CE container. The volumes are host directories and stay in place:

    docker stop gitlab
    docker rm gitlab
  3. Create the Deckhouse Code container on the same volumes, with the ports and environment variables of the GitLab CE container:

    docker run -d --name code \
      --shm-size 256m \
      -p 80:80 -p 443:443 -p 22:22 \
      -e GITLAB_OMNIBUS_CONFIG="external_url 'https://<HOSTNAME>'" \
      -v <GITLAB_HOME>/config:/etc/gitlab \
      -v <GITLAB_HOME>/logs:/var/log/gitlab \
      -v <GITLAB_HOME>/data:/var/opt/gitlab \
      <REGISTRY>/<FLAVOR>:<VERSION>
  4. Follow the start. The database migrations run during the configuration, and the instance answers 502 until it finishes:

    docker logs -f code
  5. Check that no migration is pending, that the encrypted data in the database is read with the secrets of the instance and that the instance passes the self-check:

    docker exec code gitlab-rake db:migrate:status | grep -w down
    docker exec code gitlab-rake gitlab:fe:secret:verify
    docker exec code gitlab-rake gitlab:check SANITIZE=true

    The first command prints nothing when all migrations are applied. When it prints migrations, apply them and run the checks again:

    docker exec code gitlab-rake db:migrate

Migration through a backup archive

The data moves in a backup archive, and the files the archive does not contain are copied separately. Deckhouse Code is installed in advance with the GitLab version of the source instance and the same external address.

Collecting the data of GitLab CE

Collect the files from the GitLab CE instance and keep them with the same level of protection as passwords:

  • the backup archive;
  • gitlab-secrets.json: the encryption keys of the database; without them, two-factor authentication settings, tokens and encrypted CI/CD variables are lost;
  • gitlab.rb: the instance configuration;
  • the certificates and keys from the /etc/gitlab/ssl/ directory;
  • the SSH host keys: with them, Git clients connect over SSH to Deckhouse Code without a host key warning.
  • Linux package
  • Omnibus Docker
  1. Create a backup:

    sudo gitlab-backup create

    The archive <BACKUP_NAME>_gitlab_backup.tar is written to /var/opt/gitlab/backups/.

  2. Copy the archive, the /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json files, the /etc/gitlab/ssl/ directory and the /etc/ssh/ssh_host_* files off the server.

  1. Create a backup:

    docker exec -t gitlab gitlab-backup create

    The archive <BACKUP_NAME>_gitlab_backup.tar is written to <GITLAB_HOME>/data/backups/ on the host.

  2. Copy the archive, the gitlab.rb and gitlab-secrets.json files, the ssl/ directory and the ssh_host_* files from <GITLAB_HOME>/config off the host.

When GitLab CE keeps uploads, CI/CD artifacts, LFS objects or packages in object storage, the archive does not contain them. Connect Deckhouse Code to the same buckets, or copy the buckets to the storage of Deckhouse Code, for example with rclone sync.

<BACKUP_NAME> is the archive file name without the _gitlab_backup.tar suffix: for the 1784800000_2026_07_23_19.4.1_gitlab_backup.tar archive it is 1784800000_2026_07_23_19.4.1.

Restoring into Deckhouse Code

After the restore, the database holds the schema of GitLab CE. The gitlab-rake db:migrate command adds the tables of Deckhouse Code to it.

  • Linux package
  • Omnibus Docker
  • Helm Chart
  • Deckhouse Kubernetes Platform module

Install Deckhouse Code as described in Quick start.

  1. Replace /etc/gitlab/gitlab.rb and /etc/gitlab/gitlab-secrets.json with the files of GitLab CE, copy the certificates and keys into /etc/gitlab/ssl/, then run the configuration:

    sudo gitlab-ctl reconfigure
  2. Copy the SSH host keys into /etc/ssh/ and restart the SSH server:

    sudo cp ssh_host_* /etc/ssh/
    # RED OS, AlmaLinux.
    sudo systemctl restart sshd
    # Ubuntu.
    sudo systemctl restart ssh
  3. Copy the archive to the backup directory and hand the file over to the git user:

    sudo cp <BACKUP_NAME>_gitlab_backup.tar /var/opt/gitlab/backups/
    sudo chown git:git /var/opt/gitlab/backups/<BACKUP_NAME>_gitlab_backup.tar
  4. Stop the services that write to the database:

    sudo gitlab-ctl stop puma
    sudo gitlab-ctl stop sidekiq
  5. Restore the data. The command asks for confirmation twice, before the authorized_keys file is rebuilt and before the current database tables are dropped; answer yes both times:

    sudo gitlab-backup restore BACKUP=<BACKUP_NAME>
  6. Apply the database migrations of Deckhouse Code:

    sudo gitlab-rake db:migrate
  7. Restart the instance and check it:

    sudo gitlab-ctl restart
    sudo gitlab-rake gitlab:fe:secret:verify
    sudo gitlab-rake gitlab:check SANITIZE=true

The container reads the configuration, the secrets and the SSH host keys from the configuration volume and generates host keys only when the volume has none, so the files are put on the volume before the first start.

  1. Put the files of GitLab CE into the configuration directory on the host: gitlab.rb, gitlab-secrets.json and the ssh_host_* files into /srv/code/config/, the certificates and keys into /srv/code/config/ssl/.

  2. Create the container as described in Quick start and wait for the first start to finish:

    docker logs -f code
  3. Copy the archive into the backup directory on the data volume and give it the owner the instance uses:

    sudo cp <BACKUP_NAME>_gitlab_backup.tar /srv/code/data/backups/
    docker exec code chown git:git /var/opt/gitlab/backups/<BACKUP_NAME>_gitlab_backup.tar
  4. Stop the services that write to the database:

    docker exec code gitlab-ctl stop puma
    docker exec code gitlab-ctl stop sidekiq
  5. Restore the data. The command asks for confirmation twice, before the authorized_keys file is rebuilt and before the current database tables are dropped; answer yes both times:

    docker exec -it code gitlab-backup restore BACKUP=<BACKUP_NAME>
  6. Apply the database migrations of Deckhouse Code:

    docker exec code gitlab-rake db:migrate
  7. Restart the container and check the instance:

    docker restart code
    docker exec code gitlab-rake gitlab:fe:secret:verify
    docker exec code gitlab-rake gitlab:check SANITIZE=true

Install Deckhouse Code as described in Quick start, with the release named code in the code namespace and object storage for backups configured.

  1. Convert the gitlab_rails section of gitlab-secrets.json into the Rails secrets file and replace the Secret of the release with it:

    yq -p json -o yaml '{"production": .gitlab_rails}' gitlab-secrets.json > secrets.yml
    d8 k -n code create secret generic code-rails-secret --from-file=secrets.yml \
      --dry-run=client -o yaml | d8 k apply -f -
  2. Replace the Secret with the SSH host keys:

    d8 k -n code create secret generic code-gitlab-shell-host-keys --from-file=. \
      --dry-run=client -o yaml | d8 k apply -f -

    Run the command in a directory that holds only the ssh_host_* files.

  3. Restart the pods so that they read the new Secrets:

    d8 k -n code delete pods -l app=webservice
    d8 k -n code delete pods -l app=sidekiq
    d8 k -n code delete pods -l app=gitlab-shell
  4. Upload the archive to the backups bucket, the global.appConfig.backups.bucket value.

  5. Restore the data inside the toolbox pod:

    d8 k -n code exec -it deploy/code-toolbox -- backup-utility --restore -t <BACKUP_NAME>
  6. Apply the database migrations of Deckhouse Code:

    d8 k -n code exec -it deploy/code-toolbox -- gitlab-rake db:migrate
  7. Restart the pods and check the instance:

    d8 k -n code delete pods -l app=webservice
    d8 k -n code delete pods -l app=sidekiq
    d8 k -n code exec -it deploy/code-toolbox -- gitlab-rake gitlab:fe:secret:verify
    d8 k -n code exec -it deploy/code-toolbox -- gitlab-rake gitlab:check SANITIZE=true
The module takes the backup archive from the backup bucket of the CodeInstance resource and the secrets and SSH host keys from Secrets in the d8-code namespace. The procedure is described in the Migration section of the module documentation; the Linux package installation of GitLab named there is the GitLab CE instance.

After the migration

Point the DNS name of the instance at Deckhouse Code when it runs on another server, then check the instance and finish the move:

  1. Sign in to the web interface with an account of GitLab CE and clone a repository over HTTPS and over SSH.
  2. Install the license key as described in License. Without a key, the instance runs in trial mode.
  3. When the external address has changed, update the url setting of every GitLab Runner in /etc/gitlab-runner/config.toml and restart the runner with gitlab-runner restart.

A pipeline keeps the CI_SERVER_URL, CI_PROJECT_URL and other CI_* variables of the moment it was created. When the address has changed, a retried job of a pipeline created before the migration uses the old address; run a new pipeline instead.

Additional resources