Complete guide to set up a self-hosted GitLab Runner with Docker-in-Docker support using Docker Socket Proxy for secure and isolated CI/CD execution.
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ GitLab Jobs │───▶│ GitLab Runner │───▶│ Docker Socket │
│ │ │ │ │ Proxy │
│ (Job Containers)│ │ (Management Layer) │ │ (Security Layer)│
└─────────────────┘ └──────────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Host Docker │
│ Daemon │
└─────────────────┘
# Create a custom network for container communication
docker network create proxy_network
# Run Docker Socket Proxy with necessary permissions
docker run -d \
--name docker-socket-proxy \
--restart always \
--network proxy_network \
-v /var/run/docker.sock:/var/run/docker.sock \
-e CONTAINERS=1 \
-e IMAGES=1 \
-e AUTH=1 \
-e SECRETS=1 \
-e POST=1 \
-e BUILD=1 \
-e COMMIT=1 \
-e CONFIGS=1 \
-e DISTRIBUTION=1 \
-e EXEC=1 \
-e GRPC=1 \
-e INFO=1 \
-e NETWORKS=1 \
-e NODES=1 \
-e PLUGINS=1 \
-e SERVICES=1 \
-e SESSION=1 \
-e SWARM=1 \
-e SYSTEM=1 \
-e TASKS=1 \
-e VOLUMES=1 \
-e DELETE=1 \
-p 127.0.0.1:2375:2375 \
tecnativa/docker-socket-proxy:latest
# Create directory for GitLab Runner configuration
mkdir -p /opt/gitlab-runner/config
cd /opt/gitlab-runner
# Set proper permissions
chmod 755 /opt/gitlab-runner
chmod 755 /opt/gitlab-runner/config
docker
, self-hosted
, your-custom-tag
glrt-
)# Run GitLab Runner (without Docker socket mount - using proxy instead)
docker run -d \
--name gitlab-runner \
--restart always \
--network proxy_network \
-v /opt/gitlab-runner/config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest
# Register runner with GitLab
docker exec -it gitlab-runner gitlab-runner register \
--url https://gitlab.com \
--token YOUR_REGISTRATION_TOKEN \
--name "docker-self-hosted-runner" \
--executor docker \
--docker-image docker:24.0.5 \
--docker-privileged \
--tag-list "docker,self-hosted,your-project-name" \
--docker-volumes "/cache" \
--docker-network-mode proxy_network
Replace YOUR_REGISTRATION_TOKEN
with your actual token from Step 4.
Create or edit the configuration file:
# Edit the configuration
docker exec -it gitlab-runner nano /etc/gitlab-runner/config.toml
Or create the configuration directly on the host:
# Edit on host system
nano /opt/gitlab-runner/config/config.toml
config.toml
)concurrent = 2
check_interval = 0
shutdown_timeout = 30
[session_server]
session_timeout = 1800
[[runners]]
name = "docker-self-hosted-runner"
url = "https://gitlab.com"
id = YOUR_RUNNER_ID
token = "YOUR_RUNNER_TOKEN"
token_obtained_at = 2025-05-22T22:44:58Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
tag_list = ["docker", "self-hosted", "your-project-name"]
run_untagged = false
locked = false
[runners.cache]
MaxUploadedArchiveSize = 0
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "docker:24.0.5"
privileged = true
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
network_mtu = 0
host = "tcp://docker-socket-proxy:2375"
[runners.docker.environment]
DOCKER_TLS_CERTDIR = ""
DOCKER_HOST = "tcp://docker-socket-proxy:2375"
Important: Replace YOUR_RUNNER_ID
and YOUR_RUNNER_TOKEN
with actual values from your registration.
.gitlab-ci.yml
)# Basic Docker-based CI/CD pipeline
stages:
- test
- build
- deploy
variables:
DOCKER_TLS_CERTDIR: ""
# Test stage - verify Docker functionality
test_docker:
stage: test
image: docker:24.0.5
services:
- docker:24.0.5-dind
script:
- echo "Testing Docker functionality..."
- docker --version
- docker info
- docker run --rm hello-world
- echo "✅ Docker is working correctly!"
# Build stage - example application build
build_app:
stage: build
image: docker:24.0.5
services:
- docker:24.0.5-dind
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
script:
- echo "Building application..."
- docker build -t $IMAGE_TAG .
- docker images
- echo "✅ Build completed successfully!"
only:
- main
- develop
# Deploy stage - example deployment
deploy_app:
stage: deploy
image: docker:24.0.5
services:
- docker:24.0.5-dind
script:
- echo "Deploying application..."
- echo "Application deployed successfully!"
only:
- main
when: manual
Dont forget to add a Dockerfile
so that the build step works:
# Simple test Dockerfile
FROM alpine:latest
RUN apk add --no-cache curl
WORKDIR /app
COPY . .
CMD ["echo", "Hello from Docker build!"]
# Check both containers are running
docker ps | grep -E "(gitlab-runner|docker-socket-proxy)"
# Expected output:
# gitlab-runner (running)
# docker-socket-proxy (running)
# Both containers should be on proxy_network
docker inspect gitlab-runner --format '{{.NetworkSettings.Networks}}'
docker inspect docker-socket-proxy --format '{{.NetworkSettings.Networks}}'
# Test connectivity
docker exec -it gitlab-runner sh -c "wget -qO- http://docker-socket-proxy:2375/info"
# List configured runners
docker exec -it gitlab-runner gitlab-runner list
# Verify runner in GitLab UI
# Go to Project → Settings → CI/CD → Runners
# Your runner should show as "Available" with green circle
.gitlab-ci.yml
in your repository (use sample above)# Start all services
docker start docker-socket-proxy gitlab-runner
# Stop all services
docker stop gitlab-runner docker-socket-proxy
# View logs
docker logs -f gitlab-runner
# Restart runner
docker restart gitlab-runner
# Check runner status
docker exec -it gitlab-runner gitlab-runner status
# List runners
docker exec -it gitlab-runner gitlab-runner list
/opt/gitlab-runner/
├── config/
│ ├── config.toml # Main configuration
│ ├── config.toml.backup # Backup configuration
│ └── certs/ # SSL certificates (if needed)
└── logs/ # Log files (optional)
proxy_network
)🎉 Congratulations! Your GitLab Runner setup is complete and ready for CI/CD workflows.