A Docker image based on Gitea Runner with Docker CLI, Docker Compose, and Node.js 24 LTS pre-installed, enabling your CI/CD workflows to build, run, and manage Docker containers, compose projects, and Node-based tooling.
- Base Image:
gitea/runner:2.1.0 - Docker CLI: Latest version from official Docker image (
docker:27-cli) - Docker Compose: v5.0.1 (installed as a Docker CLI plugin)
- Node.js: 24 LTS from the official Node image, including
npm,npx, andcorepack - Multi-Architecture Support: Available for
linux/amd64andlinux/arm64 - Lightweight: Optimized build with minimal layers and cleaned package caches
This image supports the following platforms:
linux/amd64(x86_64)linux/arm64(aarch64)
Docker automatically pulls the correct architecture for your system.
docker run -d \
--name gitea-runner \
-e GITEA_INSTANCE_URL="https://your-gitea-instance.com" \
-e GITEA_RUNNER_REGISTRATION_TOKEN="your-registration-token" \
-e GITEA_RUNNER_NAME="my-runner" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v ./runner-data:/data \
ghcr.io/attilaszasz/act-runner-docker-cli:latestCreate a docker-compose.yml file:
networks:
gitea:
external: true
services:
gitea:
image: docker.gitea.com/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- gitea
volumes:
- ./gitea/data:/data
- /etc/TZ:/etc/TZ:ro
- /etc/localtime:/etc/localtime:ro
ports:
- 3000:3000
- 222:22
runner:
image: ghcr.io/attilaszasz/act-runner-docker-cli:latest
container_name: gitea-runner
environment:
GITEA_INSTANCE_URL: "https://gitea.example.com"
GITEA_RUNNER_REGISTRATION_TOKEN: "your-token-here"
GITEA_RUNNER_NAME: "my-runner"
# Optional: Custom labels
# GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:20-bullseye,ubuntu-22.04:docker://node:20-bullseye"
restart: always
depends_on:
- gitea
networks:
- gitea
volumes:
- ./runner-data:/data
- /var/run/docker.sock:/var/run/docker.sockThen start the services:
docker-compose up -d| Variable | Description | Example |
|---|---|---|
GITEA_INSTANCE_URL |
URL of your Gitea instance | https://gitea.example.com |
GITEA_RUNNER_REGISTRATION_TOKEN |
Runner registration token from Gitea | Obtained from Gitea Admin → Actions → Runners |
| Variable | Description | Default |
|---|---|---|
GITEA_RUNNER_NAME |
Display name for the runner | Auto-generated |
GITEA_RUNNER_LABELS |
Custom runner labels for job targeting | Default labels from base image |
- Log in to your Gitea instance as an administrator
- Navigate to Site Administration → Actions → Runners
- Click Create new Runner
- Copy the registration token displayed
- Use this token in the
GITEA_RUNNER_REGISTRATION_TOKENenvironment variable
-
Docker Socket:
/var/run/docker.sock:/var/run/docker.sock- Allows the runner to communicate with the Docker daemon
- Important: This grants the container full control over your Docker host
-
Data Directory:
./runner-data:/data- Stores runner configuration and state
- Persists across container restarts
- Timezone:
/etc/TZ:/etc/TZ:roand/etc/localtime:/etc/localtime:ro- Synchronizes container timezone with host
Runner labels determine which jobs the runner can execute. You can customize labels using the GITEA_RUNNER_LABELS environment variable:
GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:20-bullseye,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04"label-name:docker://image-name
- label-name: The label used in workflow files (
runs-on: label-name) - image-name: The Docker image to use for jobs with this label
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp .
- name: Run with Docker Compose
run: docker compose up -dThis image requires access to the Docker socket (/var/run/docker.sock), which grants full control over the Docker daemon. Consider the following:
- Trusted Workflows Only: Only run workflows you trust
- Isolated Network: Use a dedicated Docker network for the runner
- Resource Limits: Set memory and CPU limits in your compose file:
services:
runner:
# ... other config ...
deploy:
resources:
limits:
cpus: '2'
memory: 4G- Rootless Docker: For improved security, consider using rootless Docker mode
- Private Repositories: Restrict runner access to private repositories only
-
Check Network: Ensure the runner can reach
GITEA_INSTANCE_URLdocker exec gitea-runner curl -I https://your-gitea-instance.com -
Verify Token: Check that
GITEA_RUNNER_REGISTRATION_TOKENis correct -
Check Logs:
docker logs gitea-runner
-
Verify Socket Mount: Ensure
/var/run/docker.sockis mounteddocker exec gitea-runner ls -l /var/run/docker.sock -
Check Permissions: The runner needs access to the Docker socket
-
Test Docker Access:
docker exec gitea-runner docker ps
Verify Docker Compose is installed correctly:
docker exec gitea-runner docker compose versionShould output: Docker Compose version v5.0.1
Verify Node.js is installed correctly:
docker exec gitea-runner node --version
docker exec gitea-runner npm --versionShould report a Node.js 24.x release and the bundled npm version.
This runner is perfect for implementing GitOps-style continuous deployment in your homelab. Push changes to your Git repository, and automatically deploy your services to your homelab server.
- Push to Git: Commit and push changes to your Gitea repository
- Trigger Workflow: Gitea Actions automatically runs your deployment workflow
- Deploy Services: The runner executes
docker composecommands on your homelab host - Automatic Updates: Your services are updated without manual intervention
Organize your infrastructure repository:
homelab/
├── .gitea/
│ └── workflows/
│ └── deploy.yml
├── docker-compose.yml
├── .env.example
└── README.md
Create .gitea/workflows/deploy.yml:
name: Deploy to Homelab
on:
push:
branches:
- main
jobs:
deploy:
# Target your specific runner using custom labels
runs-on: [ubuntu-22.04, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create .env file from secrets
run: |
echo "TZ=${{ vars.TZ }}" > .env
echo "PUID=${{ vars.PUID }}" >> .env
echo "PGID=${{ vars.PGID }}" >> .env
echo "DOMAIN=${{ vars.DOMAIN }}" >> .env
echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> .env
echo "API_KEY=${{ secrets.API_KEY }}" >> .env
- name: Deploy services
run: docker compose up -d --remove-orphans
- name: Clean up old images
run: docker image prune -af --filter "until=72h"Set a custom label to target your homelab runner specifically. In your docker-compose.yml:
runner:
image: ghcr.io/attilaszasz/act-runner-docker-cli:latest
environment:
GITEA_INSTANCE_URL: "https://gitea.example.com"
GITEA_RUNNER_REGISTRATION_TOKEN: "your-token-here"
GITEA_RUNNER_NAME: "homelab-runner"
# Add custom label to identify this runner
GITEA_RUNNER_LABELS: "ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,homelab:host"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./runner-data:/dataLabel Explanation:
ubuntu-22.04:docker://...- Run jobs in a Docker containerhomelab:host- Run jobs directly on the host (necessary for docker compose deployments)
In Gitea, navigate to your repository → Settings → Actions → Secrets and Variables
Variables (non-sensitive configuration):
TZ=America/New_York
PUID=1000
PGID=1000
DOMAIN=example.com
Secrets (sensitive data):
DB_PASSWORD=your-secure-password
API_KEY=your-api-key
NOTIFICATION_TOKEN=your-token
Your docker-compose.yml in the repository:
version: '3.8'
services:
app:
image: myapp:latest
container_name: myapp
environment:
- TZ=${TZ}
- PUID=${PUID}
- PGID=${PGID}
volumes:
- ./data:/data
ports:
- "8080:8080"
restart: unless-stopped
db:
image: postgres:16
container_name: myapp-db
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
restart: unless-stoppedDeploy to different environments based on branches:
name: Deploy
on:
push:
branches:
- main
- staging
jobs:
deploy:
runs-on: [ubuntu-22.04, homelab]
steps:
- uses: actions/checkout@v4
- name: Set environment
run: |
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
echo "ENV=production" >> $GITHUB_ENV
echo "COMPOSE_FILE=docker-compose.yml" >> $GITHUB_ENV
else
echo "ENV=staging" >> $GITHUB_ENV
echo "COMPOSE_FILE=docker-compose.staging.yml" >> $GITHUB_ENV
fi
- name: Deploy to ${{ env.ENV }}
run: docker compose -f ${{ env.COMPOSE_FILE }} up -dname: Deploy with Monitoring
on:
push:
branches: [main]
jobs:
deploy:
runs-on: [ubuntu-22.04, homelab]
steps:
- uses: actions/checkout@v4
- name: Deploy services
run: docker compose up -d --remove-orphans
- name: Wait for services to be healthy
run: |
echo "Waiting for services to be healthy..."
timeout 60 docker compose ps | grep -q "(healthy)" || exit 1
- name: Send success notification
if: success()
run: |
curl -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=Deployment Success" \
-F "message=Services deployed successfully to homelab" \
-F "priority=5"
- name: Send failure notification
if: failure()
run: |
curl -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
-F "title=Deployment Failed" \
-F "message=Deployment to homelab failed. Check logs." \
-F "priority=10"name: Deploy with Backup
on:
push:
branches: [main]
jobs:
deploy:
runs-on: [ubuntu-22.04, homelab]
steps:
- uses: actions/checkout@v4
- name: Backup current state
run: |
mkdir -p backups
docker compose config > backups/compose-$(date +%Y%m%d-%H%M%S).yml
docker compose ps > backups/services-$(date +%Y%m%d-%H%M%S).txt
- name: Deploy services
run: docker compose up -d --remove-orphans
- name: Rollback on failure
if: failure()
run: |
echo "Deployment failed, rolling back..."
docker compose down
# Restore previous version here if needed-
Version Control Everything
- Store all configuration in Git
- Use
.env.examplewith dummy values (commit this) - Never commit
.envwith real secrets (add to.gitignore)
-
Use Repository Secrets
- Store sensitive data in Gitea's secret manager
- Reference secrets in workflows, not in compose files
- Rotate secrets regularly
-
Test Before Deploying
- Use separate staging environments when possible
- Validate compose files:
docker compose config - Test workflows on feature branches
-
Monitor and Log
- Set up notifications for deployment status
- Keep deployment logs
- Monitor service health after deployment
-
Backup Strategy
- Backup volumes before updates
- Store backups outside the deployment directory
- Test restore procedures regularly
-
Network Security
- Use dedicated Docker networks
- Limit runner access to necessary resources
- Keep runner on isolated network segment if possible
-
Documentation
- Document your workflow in repository README
- Keep track of dependencies
- Document rollback procedures
Workflow not triggering:
- Verify Actions are enabled in repository settings
- Check that workflow file is in
.gitea/workflows/directory - Ensure YAML syntax is correct
Runner not picking up jobs:
- Verify runner labels match
runs-onin workflow - Check runner is connected:
docker logs gitea-runner - Ensure runner has necessary permissions
Deployment fails with permission errors:
- Check Docker socket permissions
- Verify PUID/PGID values are correct
- Ensure directories exist and are writable
Services don't update:
- Force recreate:
docker compose up -d --force-recreate - Check if images are pulled: add
docker compose pullstep - Verify compose file changes are committed
Clone the repository and build the image:
git clone https://github.com/attilaszasz/act-runner-docker-cli.git
cd act-runner-docker-cli
docker build -t act-runner-docker-cli .To build for multiple architectures:
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/attilaszasz/act-runner-docker-cli:latest \
--push .- Act Runner: 0.2.13
- Docker CLI: 27
- Docker Compose: 5.0.1
- Base OS: Ubuntu 24.04 LTS (build stage)
To update to the latest version:
docker pull ghcr.io/attilaszasz/act-runner-docker-cli:latest
docker-compose up -dContributions are welcome! Please feel free to submit a Pull Request.
This project builds upon the official Gitea Runner. See the Gitea Runner repository for license information.
- Gitea - Self-hosted Git service
- Gitea Runner - Official Gitea Actions runner
- Issues: GitHub Issues
- Gitea Actions Documentation: docs.gitea.com