Back to articles
Technology Insight

Building a Lightweight DevOps Pipeline: Deploying Gitea and Woodpecker CI on a Minimal VPS

May 27, 2026

Introduction: The Challenge of Resource-Efficient DevOps

In modern software development, establishing a robust Continuous Integration and Continuous Delivery (CI/CD) pipeline is no longer a luxury—it is a necessity. However, for small teams, independent developers, and startups, traditional enterprise solutions like self-hosted GitLab or Jenkins pose a significant challenge. These platforms are notoriously resource-intensive, often requiring multiple gigabytes of RAM and substantial CPU overhead just to run idle instances. On a budget Virtual Private Server (VPS) with minimalist configurations (such as 1 CPU core and 1GB to 2GB of RAM), running these heavy tools can lead to frequent out-of-memory errors, system instability, and inflated operational costs.

Fortunately, the open-source ecosystem offers a powerful, lightweight alternative: combining Gitea for source code management with Woodpecker CI for automated pipelines. Both tools are written in Go, compiled to native binaries, and designed explicitly for high efficiency and low memory footprints. This comprehensive guide walks you through planning, deploying, and optimizing an internal Git and CI/CD infrastructure on a minimal VPS, enabling enterprise-grade automation without the enterprise hardware price tag.

1. Architectural Overview: Why Gitea and Woodpecker CI?

Before diving into the installation, it is crucial to understand why this specific stack is ideal for a resource-constrained environment.

Gitea: The Lightweight Git Core

Gitea is a community-managed fork of Gogs, designed to provide a self-hosted Git service that mimics the user experience of GitHub. Unlike GitLab, which bundles an extensive array of Ruby, Go, and PostgreSQL dependencies, Gitea operates as a single, highly optimized binary. It consumes as little as 50MB to 100MB of RAM at idle, making it perfectly suited for low-end virtual machines.

Woodpecker CI: The Community-Driven CI/CD Engine

Woodpecker CI is an independent fork of Drone CI (specifically from its open-source era). It utilizes a simple server-agent architecture where pipelines are defined using standard YAML syntax. Every pipeline step executes inside an isolated Docker container, ensuring reproducibility. While enterprise CI engines require significant baseline resources, a Woodpecker agent and server can comfortably operate on minimal RAM, scaling up resource consumption only during active build jobs.

2. Prerequisites and System Preparation

To successfully follow this guide, ensure your environment meets the following baseline criteria:

  • Hardware: A Linux VPS with at least 1 vCPU, 1GB of RAM, and 20GB of SSD storage. (A small swap space of 1GB to 2GB is highly recommended to handle temporary build spikes).
  • Operating System: Ubuntu 22.04 LTS or Debian 12 preferred.
  • Domain & Networking: A fully qualified domain name (FQDN) with A records pointing to your VPS public IP address (e.g., git.example.com and ci.example.com).
  • Docker Installed: Docker Engine and Docker Compose (v2) must be pre-installed on the host system.
Note on Security: Ensure your cloud provider's firewall allows incoming traffic on ports 80 (HTTP), 443 (HTTPS), and 22 (SSH for code pushing).

3. Step-by-Step Deployment via Docker Compose

Using Docker Compose ensures our entire infrastructure is declared cleanly in a single file, simplifying container management, data persistence, and networking.

The Compose Configuration File

Create a directory named /opt/devops-stack and place the following docker-compose.yml file inside it. This setup includes Gitea, Woodpecker Server, Woodpecker Agent, and a PostgreSQL database as the unified backend.

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    container_name: devops_db
    restart: always
    environment:
      POSTGRES_USER: devops_user
      POSTGRES_PASSWORD: StrongSecurePassword123
      POSTGRES_DB: gitea_db
    volumes:
      - ./postgres_data:/var/lib/postgresql/data

  gitea:
    image: gitea/gitea:1.21-rootless
    container_name: devops_gitea
    restart: always
    depends_on:
      - db
    environment:
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea_db
      - GITEA__database__USER=devops_user
      - GITEA__database__PASSWD=StrongSecurePassword123
    volumes:
      - ./gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:22"

  woodpecker-server:
    image: woodpeckerci/woodpecker-server:v2.4
    container_name: woodpecker_server
    restart: always
    depends_on:
      - gitea
    environment:
      - WOODPECKER_GITEA=true
      - WOODPECKER_GITEA_URL=http://gitea:3000
      - WOODPECKER_GITEA_CLIENT=YOUR_OAUTH_CLIENT_ID
      - WOODPECKER_GITEA_SECRET=YOUR_OAUTH_CLIENT_SECRET
      - WOODPECKER_HOST=[https://ci.example.com](https://ci.example.com)
      - WOODPECKER_AGENT_SECRET=AgentCommunicationSecretABC987
    volumes:
      - ./woodpecker_server_data:/var/lib/woodpecker
    ports:
      - "8000:8000"

  woodpecker-agent:
    image: woodpeckerci/woodpecker-agent:v2.4
    container_name: woodpecker_agent
    restart: always
    depends_on:
      - woodpecker-server
    environment:
      - WOODPECKER_SERVER=woodpecker-server:8000
      - WOODPECKER_AGENT_SECRET=AgentCommunicationSecretABC987
      - WOODPECKER_MAX_WORKERS=1
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

4. Configuring Gitea and Woodpecker Integration

To establish a secure connection between Gitea and Woodpecker CI, we utilize OAuth2. This prevents the need to share root administrator passwords between applications.

  1. Initialize Gitea: Run docker compose up -d gitea db, visit http://:3000, and complete the web-based installation wizard. Create your primary administrator account.
  2. Register an OAuth2 Application: Inside Gitea, navigate to Site Administration > Applications (or User Settings > Applications). Create a new OAuth2 application named "Woodpecker CI".
  3. Set Redirect URI: Set the Redirect URI precisely to [https://ci.example.com/authorize](https://ci.example.com/authorize).
  4. Update Environment Variables: Copy the generated Client ID and Client Secret from Gitea, paste them into the WOODPECKER_GITEA_CLIENT and WOODPECKER_GITEA_SECRET fields in your docker-compose.yml file, and restart the full stack using docker compose up -d.

5. Creating Your First Automated Pipeline

With both services linked, creating a continuous integration pipeline is straightforward. Woodpecker reads pipeline instructions from a configuration file located at the root of your repository.

Create a file named .woodpecker.yml in your repository and populate it with the following basic test and build instructions:

when:
  event: [push, pull_request]

steps:
  test:
    image: node:18-alpine
    commands:
      - npm install
      - npm run test

  build:
    image: docker:stable
    commands:
      - echo "Compiling assets and building application package..."

Whenever a developer pushes code to Gitea, a webhook notifies Woodpecker Server, which assigns the pipeline tasks to the Woodpecker Agent. The agent spins up the defined isolated environments (Node and Docker), executes the code, and reports the real-time status back to the Gitea UI interface.

6. VPS Optimization Strategies for Low-Spec Hosting

Operating a CI/CD infrastructure on a minimalist server requires aggressive resource optimization to ensure long-term stability and high uptime.

Restricting Concurrent Build Workers

In our configuration file, the environment variable WOODPECKER_MAX_WORKERS=1 is explicit. Allowing multiple parallel builds on a 1GB RAM machine will trigger the Linux Kernel Out-Of-Memory (OOM) killer, crashing your database or web server. Forcing a sequential, single-worker execution queue maintains predictable resource usage.

Implementing Linux Swap Space

If a build step temporarily requires more memory than the physical RAM can provide, having a backup swap space prevents container failure. To configure a 2GB swap file on your VPS, execute the following commands in your host terminal:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Automated Docker Cleanup

Over time, Woodpecker builds will leave behind cached layers, dangling volumes, and old images that can quickly fill up your SSD. It is highly recommended to schedule a daily cron job that executes docker system prune -af --volumes during off-peak hours to keep disk space utilization consistently low.

Conclusion

Deploying a private Git server and an automated CI/CD pipeline does not require costly enterprise cloud infrastructure. By pairing the extreme optimization of Gitea with the minimalist modularity of Woodpecker CI, you can achieve a sophisticated, modern DevOps workflow on a basic VPS. This setup minimizes operational overhead, ensures data ownership, and maintains lightning-fast performance for small development teams.

Building a Lightweight DevOps Pipeline: Deploying Gitea and Woodpecker CI on a Minimal VPS | DPTCloud