Back to articles
Technology Insight

Streamlining CI/CD: How to Configure Gitea Actions for Self-Hosted Docker Builds and GHCR Deployment

May 29, 2026

Introduction

In the modern DevOps landscape, establishing an efficient, secure, and cost-effective Continuous Integration and Continuous Deployment (CI/CD) pipeline is crucial for software development teams. While major platforms like GitHub Actions and GitLab CI dominate the market, many organizations and independent developers prefer maintaining control over their infrastructure. Leveraging a self-hosted Virtual Private Server (VPS) offers unparalleled privacy, data sovereignty, and cost predictability.

Historically, hosting your own git repository meant sacrificing advanced automation unless you integrated complex external tools. However, with the evolution of Gitea Actions—a built-in CI/CD engine compatible with GitHub Actions workflow syntax—self-hosted automation has become incredibly accessible. This comprehensive guide will demonstrate exactly how to configure Gitea Actions on your private VPS to automatically build, package, and push Docker images directly to the GitHub Container Registry (GHCR).

---

Understanding the Architecture

Before diving into the configuration, it is essential to understand how the components interact in this setup. Our architecture consists of three core pillars:

  • Gitea Instance: Hosted on your VPS, serving as the central version control system where your source code resides.
  • Gitea Runner (Act Runner): A lightweight daemon running on your VPS that listens for workflow jobs triggered by code commits or pull requests, executing them inside isolated environments.
  • GitHub Container Registry (GHCR): The target registry hosted by GitHub where our final production-ready Docker images will be securely stored and versioned.
By processing the build workloads directly on your self-hosted VPS while utilizing GHCR for highly available image distribution, you achieve an optimal balance between private infrastructure control and robust delivery mechanics.
---

Prerequisites

To follow this tutorial successfully, ensure you have the following prerequisites in place:

  1. A private VPS running a modern Linux distribution (e.g., Ubuntu 22.04 LTS or later) with a public IP address and a domain configured for your Gitea instance.
  2. Docker and Docker Compose installed on your VPS.
  3. A fully functional Gitea instance (version 1.19 or higher, as Gitea Actions requires modern engine support).
  4. A GitHub account with a Personal Access Token (PAT) generated. This token requires the write:packages, read:packages, and repo scopes to allow the VPS runner to authenticate and push images to GHCR.
---

Step 1: Setting Up and Registering the Gitea Runner

Gitea utilizes a separate component called act_runner to execute workflows. Follow these steps to deploy and register the runner on your VPS.

1.1 Enable Actions in Gitea

First, ensure that Actions are enabled globally in your Gitea configuration file (app.ini). Open the configuration and verify or add the following section:

[actions]
ENABLED = true

Restart your Gitea service to apply the changes. Next, navigate to your Gitea site administration panel, go to Actions > Runners, and click Create New Runner. Copy the registration token provided; you will need it in the next step.

1.2 Deploy the Runner via Docker Compose

Create a dedicated directory on your VPS for the runner and establish a docker-compose.yml file to manage it seamlessly:

version: '3.8'

services:
  runner:
    image: gitea/act_runner:latest
    environment:
      - GITEA_INSTANCE_URL=[https://your-gitea-domain.com](https://your-gitea-domain.com)
      - GITEA_RUNNER_REGISTRATION_TOKEN=YOUR_COPIED_REGISTRATION_TOKEN
      - GITEA_RUNNER_NAME=vps-ci-runner
      - GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye
    volumes:
      - ./data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    restart: always

Run docker compose up -d to start the runner. Check your Gitea admin panel to confirm that the runner appears online and is ready to accept jobs.

---

Step 2: Configuring Secrets in Your Gitea Repository

To securely connect your self-hosted VPS to the GitHub Container Registry, you must store sensitive credentials safely. Hardcoding credentials into your workflow files is a severe security risk.

Navigate to your specific repository in Gitea, then go to Settings > Actions > Secrets. Add the following two secrets:

  • GH_USERNAME: Your GitHub account username or organization name.
  • GH_PAT: The GitHub Personal Access Token generated during the prerequisite phase.
---

Step 3: Creating the Gitea Actions Workflow

Gitea Actions seamlessly interprets the declarative YAML syntax used by GitHub Actions. Create a file named .gitea/workflows/build-push.yml within the root directory of your project repository.

Insert the following configuration details into the file:

name: Build and Push Docker Image to GHCR

on:
  push:
    branches:
      - main
    tags:
      - 'v*'

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ secrets.GH_USERNAME }}
          password: ${{ secrets.GH_PAT }}

      - name: Extract Docker Metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ghcr.io/${{ secrets.GH_USERNAME }}/my-app
          tags: |
            type=semver,pattern={{version}}
            type=sha,format=short
            type=ref,event=branch

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
---

Step 4: Executing and Verifying the Pipeline

With the configuration finalized, commit the workflow file and push it to your main branch on your Gitea repository:

git add .gitea/workflows/build-push.yml
git commit -m "ci: add Gitea Actions workflow for GHCR deployment"
git push origin main

Once pushed, navigate to the Actions tab of your repository in the Gitea web interface. You will observe the workflow initialize in real-time. The runner on your VPS will intercept the task, pull your code, execute the Docker build command locally, authenticate against GitHub, and push the compiled image layers to ghcr.io.

To verify successful delivery, log into your GitHub account, look under your profile packages or organization tab, and check for the presence of the newly minted Docker container image tagged with either the branch name or commit SHA.

---

Best Practices for Production Environments

While the basic setup provides immediate functionality, optimizing your self-hosted CI/CD workflow ensures long-term stability and security:

  • Implement Cache Management: Notice the cache-from and cache-to parameters in our workflow. Utilizing layer caching drastically speeds up subsequent builds by reusing untouched instructions, preserving precious VPS CPU cycles and bandwidth.
  • Restrict Runner Permissions: Run the act_runner container under a dedicated, non-root system user wherever possible, and restrict access to the host's docker.sock to authorized applications only.
  • Monitor Disk Space: Continuous Docker building creates dangling images and build caches on your VPS over time. Implement a automated cron job executing docker system prune -f periodically to prevent disk exhaustion.
---

Conclusion

By coupling the lightweight efficiency of Gitea Actions with the scalable distribution architecture of the GitHub Container Registry, you construct an enterprise-grade CI/CD pipeline directly on your private VPS. This hybrid framework eliminates reliance on external execution credits, enhances resource utilization, and ensures your application artifacts remain secure and version-controlled. Implement this pipeline today to unlock autonomous deployment workflows custom-tailored to your infrastructure constraints.

Streamlining CI/CD: How to Configure Gitea Actions for Self-Hosted Docker Builds and GHCR Deployment | DPTCloud