How to Deploy GitLab-JH on Windows with Docker Desktop and Migrate Data

This article focuses on quickly deploying GitLab-JH on Windows with Docker Desktop and completing backup restoration and configuration migration. It addresses the pain points of complex local installation, heavy upgrade dependencies, and unclear migration workflows. Keywords: Windows, Docker, GitLab.

Technical Specifications at a Glance

Parameter Value
Deployment Target GitLab-JH 17.3.1-jh.0
Operating System Windows
Container Runtime Docker Desktop
Underlying Dependency WSL 2
Access Protocols HTTP / HTTPS / SSH
Port Mapping 80, 443, 2222
Data Volumes config, logs, data
Image Registry registry.gitlab.cn/omnibus/gitlab-jh:17.3.1-jh.0
Star Count Not provided in the source
Core Dependencies Docker Desktop, PowerShell, GitLab Omnibus

This is a More Reliable Way to Deploy GitLab on Windows

Installing GitLab directly on Windows is costly and often suffers from compatibility issues. A Docker-based approach packages the runtime environment inside a container, which makes rebuilds, migration, and backups much easier.

The original implementation uses the GitLab-JH image, the Jihu edition of GitLab. It is well suited for teams that need a Chinese-language ecosystem and local private deployment. The core idea is simple: Windows handles Docker and directory mounts, while all GitLab services run inside the container.

image AI Visual Insight: This image shows the Docker Desktop download entry point, indicating that deployment starts by installing the desktop container runtime on the Windows host rather than installing the Linux version of Docker Engine directly.

Start by Preparing Docker Desktop and WSL Dependencies

When installing Docker Desktop, you may need to upgrade Windows first if your system version is too old. If Docker reports that the WSL version is outdated after startup, you can update it directly in PowerShell.

wsl --update  # Update WSL components to satisfy Docker Desktop runtime requirements

This command resolves Docker Desktop dependency issues related to WSL 2.

image AI Visual Insight: This image reflects the state after the WSL update completes, confirming that the Linux subsystem required by Docker now meets the runtime requirements and that containers can be created and started normally.

Pulling the GitLab-JH Image and Preparing Persistent Directories is a Critical Step

Pulling a fixed image version provides two benefits: the deployment becomes reproducible, and migration or troubleshooting will not be affected by unexpected changes in the latest release.

docker pull registry.gitlab.cn/omnibus/gitlab-jh:17.3.1-jh.0  # Pull a fixed image version

This command downloads the image for GitLab-JH 17.3.1.

image AI Visual Insight: This image shows the image pull process, indicating that Docker is downloading the GitLab image layer by layer from the remote registry. Because the image is usually large, download time depends heavily on network conditions and disk performance.

Next, create three host directories to store configuration, logs, and application data. This ensures that repository data and configuration remain intact even if the container is removed.

mkdir D:\gitlab\config  # Store GitLab configuration files
mkdir D:\gitlab\logs    # Store runtime logs
mkdir D:\gitlab\data    # Store repositories, database files, and other core data

These commands create the persistent mount directories required by GitLab.

You Can Complete the Main Deployment by Starting the Container with Port Mapping and Volume Mounts

The container startup command is the most important part of the entire process. It defines the service name, hostname, restart policy, ports, mounted directories, and external access settings.

docker run -d `
  --name gitlab-jh `                         # Specify the container name
  --hostname gitlab-jh `                    # Specify the container hostname
  --restart always `                        # Restart the container automatically after Docker restarts
  -p 80:80 `                                # Map the HTTP port
  -p 443:443 `                              # Map the HTTPS port
  -p 2222:22 `                              # Map host port 2222 to container SSH port 22
  -v D:\gitlab\config:/etc/gitlab `       # Mount the configuration directory
  -v D:\gitlab\logs:/var/log/gitlab `     # Mount the log directory
  -v D:\gitlab\data:/var/opt/gitlab `     # Mount the data directory
  --shm-size 256m `                         # Set shared memory to avoid issues in some services
  -e GITLAB_OMNIBUS_CONFIG="external_url 'http://localhost'; gitlab_rails['gitlab_shell_ssh_port'] = 2222" `  # Set the external URL and SSH port
  registry.gitlab.cn/omnibus/gitlab-jh:17.3.1-jh.0

This command performs a standardized GitLab container startup with persistent storage bindings.

After startup, the command returns quickly, but GitLab internal initialization can take several minutes or longer. Keep monitoring the logs until all components report ready.

docker logs -f gitlab-jh  # Follow container startup logs in real time

This command helps you determine whether GitLab has finished initializing.

image AI Visual Insight: This image shows the GitLab container logs entering a stable output phase, which usually means that critical components such as Puma, Sidekiq, and Nginx have started successfully and the service is close to being available.

image AI Visual Insight: This image shows the container running in Docker Desktop, confirming that the container lifecycle is healthy at the host level. However, actual availability should still be verified through logs and browser access.

Initial Access and Retrieving the Root Password Determine Whether You Can Log In

After you open http://localhost in a browser, a normal page response indicates that the web service is reachable.

image AI Visual Insight: This image shows the first GitLab access page, confirming that HTTP port mapping is working and that the external URL configuration is correctly connected to the web service inside the container.

For the first login, you need the initial root password, which you can read from a file inside the container.

docker exec -it gitlab-jh cat /etc/gitlab/initial_root_password  # Read the initial root password

This command retrieves the credentials required for the first GitLab login.

image AI Visual Insight: This image shows the result of reading the initial password file inside the container, indicating that GitLab has completed account initialization and that the administrator can now sign in.

After login, you can access the GitLab home page and start creating users, projects, or importing repositories.

image AI Visual Insight: This image shows the main GitLab dashboard after login, indicating that the instance has completed initialization and is ready for core tasks such as repository management, permission assignment, and CI/CD configuration.

Data Migration Essentially Means Restoring Backups and Synchronizing Configuration Files

If you already have a backup from an older instance, the restore process must do more than restore backup.tar. You must also synchronize gitlab.rb and gitlab-secrets.json. Otherwise, common outcomes include failed logins or decryption errors for sensitive data.

It is Safer to Stop Critical Services Before Running the Restore

docker exec -it gitlab-jh gitlab-ctl stop puma     # Stop the web process
docker exec -it gitlab-jh gitlab-ctl stop sidekiq  # Stop the background job process

These commands freeze core services before restoration and reduce write conflicts.

image AI Visual Insight: This image reflects the GitLab service stop process, showing that restoration does not directly overwrite a live instance but instead runs in a controlled state.

After placing the backup file in D:\gitlab\data\backups, you need to correct file ownership. Otherwise, the restore command may not be able to read the file.

docker exec -it gitlab-jh chown git:git /var/opt/gitlab/backups/*.tar  # Fix backup file ownership

This command ensures that the GitLab process has permission to access the backup file.

image AI Visual Insight: This image shows the execution feedback after backup file ownership is corrected, confirming that file ownership normalization inside the container has been completed, which is a common prerequisite for GitLab backup restoration.

When restoring, specify the backup filename prefix after BACKUP=, not the full filename.

docker exec -it gitlab-jh gitlab-backup restore BACKUP=backup_filename_prefix  # Restore a backup by prefix

This command restores business data such as projects, repositories, databases, and attachments.

image AI Visual Insight: This image shows the GitLab backup restoration process, indicating that the instance is writing database contents, repositories, and attachments from the backup back into the data volume mounted by the current container.

After copying gitlab.rb and gitlab-secrets.json into D:\gitlab\config, regenerate the configuration and restart the services.

docker exec -it gitlab-jh gitlab-ctl reconfigure  # Reload configuration and generate runtime parameters
docker exec -it gitlab-jh gitlab-ctl restart      # Restart all GitLab components

These commands make the restored configuration match the restored application data again.

After Deployment, You Should Verify These Three Capabilities First

First, confirm that localhost is accessible in a browser. Second, verify that the SSH clone URL shows port 2222. Third, ensure that restored users, projects, permissions, and repository history all match the source instance.

If you need to migrate to another machine later, you only need to copy the config, logs, and data directories and keep the same container parameters. This greatly reduces rebuild costs. That is also the biggest advantage of containerized GitLab deployment in Windows environments.

FAQ

1. Why can’t I open GitLab in the browser immediately after the container starts?

Because docker run returning only means the container has been created. It does not mean that GitLab internal services have finished initialization. You should keep watching docker logs -f gitlab-jh, and in most cases you need to wait several minutes or longer.

2. Why can’t I log in even after restoring the backup file?

Because GitLab authentication and sensitive data depend on configuration files such as gitlab-secrets.json. If you restore only the application backup without restoring the configuration, key mismatches often cause authentication failures.

3. Why is Docker deployment recommended on Windows instead of native installation?

Because Docker provides stronger isolation, simpler upgrades and rollbacks, clearer data directories, and a more portable environment for replication and disaster recovery. This makes it especially suitable for individual developers and small teams running private GitLab instances.

Core Summary: This article reconstructs the complete process for deploying GitLab-JH on Windows with Docker Desktop, covering Docker installation, WSL updates, image pulling, container startup, first login, initial password retrieval, and backup restoration, while also adding key command explanations, port mapping logic, and answers to common questions.