On the control machine :
- Linux
ansibleinstalled- SSH key set up to access the target server
On the target server :
- Docker installed and working
- SSH access using your SSH key
- GitLab instance and a Runner Registration Token
On your control machine:
after cloning the project,
You should see something like:
tree
.
├── ansible.cfg
├── inventory
│ ├── hosts.example.ini
│ └── vars.example.yml
├── playbook
│ └── deploy_runner.yml
├── README.md
└── roles
├── gitlab_runner
└── runner_prepONLY customize:
inventory/hosts.iniinventory/vars.yml
From repo root:
cp inventory/hosts.example.ini inventory/hosts.iniThen edit inventory/hosts.ini:
[runner_server]
YOUR_SERVER_IP ansible_user=YOUR_USERNAME ansible_port=22 ansible_ssh_private_key_file=~/.ssh/id_ed25519Examples:
-
For a server with custom SSH port
2222:[runner_server] 203.0.113.50 ansible_user=deploy ansible_port=2222 ansible_ssh_private_key_file=~/.ssh/id_rsa
🧠 The
ansible_userhere will be used to build the runner paths:/home/<ansible_user>/gitlab-runner, etc.
cp inventory/vars.example.yml inventory/vars.ymlEdit inventory/vars.yml and set at least:
############################################################
# GitLab Server Information
############################################################
gitlab_url: "https://your.gitlab.url"
gitlab_registration_token: "YOUR_GITLAB_REGISTRATION_TOKEN"Optional (but recommended) edits:
gitlab_runner_name→ how the runner appears in GitLabgitlab_runner_tags→ what tags this runner will have
Do NOT change the paths unless you know what you’re doing:
runner_root_dir: "/home/{{ ansible_user }}/gitlab-runner"
runner_config_dir: "/home/{{ ansible_user }}/gitlab-runner/config"
runner_cache_dir: "/home/{{ ansible_user }}/gitlab-runner/cache"
docker_compose_file: "/home/{{ ansible_user }}/gitlab-runner/docker-compose.yml"These ensure that the runner is installed under the home directory of your SSH user.
From repo root:
ansible -i inventory/hosts.ini runner_server -m pingExpected output:
YOUR_SERVER_IP | SUCCESS => {
"changed": false,
"ping": "pong"
}
Once ping works, do a dry run:
ansible-playbook playbook/deploy_runner.yml --check --diffThis will:
- Create / validate directories under
/home/<ansible_user>/gitlab-runner - Render
docker-compose.yml(but not actually start containers) - Prepare registration command (but not execute it)
- Skip validation in check mode
No changes are applied to the server in --check mode.
If you see failed=0 in the recap → you’re good.
Now run the playbook for real:
ansible-playbook playbook/deploy_runner.ymlThis will:
- Ensure directories exist under
/home/<ansible_user>/gitlab-runner - Render
docker-compose.ymlfor the GitLab Runner container - Register the runner using the official
gitlab/gitlab-runnerDocker image - Start the runner with
docker compose up -d - Validate that the runner container is running
You should see something like:
PLAY RECAP
YOUR_SERVER_IP : ok=XX changed=YY failed=0 ...
SSH into the server:
ssh -p YOUR_PORT YOUR_USERNAME@YOUR_SERVER_IPCheck containers:
docker psYou should see:
CONTAINER ID IMAGE COMMAND STATUS NAMES
xxxxxx gitlab/gitlab-runner:latest "/usr/bin/dumb-init …" Up XX minutes gitlab-runner
List runners inside the container:
docker exec -it gitlab-runner gitlab-runner listYou should see your runner, e.g.:
docker-dind-runner Executor=docker URL=https://your.gitlab.url
Go to your GitLab instance:
-
Open your project
-
Go to Settings → CI/CD
-
Expand the Runners section
-
Under Available specific runners or Group/Instance Runners, you should see:
- Name: your
gitlab_runner_name(e.g.docker-dind-runner) - Status: Online
- Tags: whatever you set in
gitlab_runner_tags
- Name: your
If it’s online here → your runner is fully ready to run jobs.