Building a Lightweight DevOps Pipeline: Deploying Gitea and Woodpecker CI on a Minimal VPS
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.comandci.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.
- Initialize Gitea: Run
docker compose up -d gitea db, visithttp://, and complete the web-based installation wizard. Create your primary administrator account.:3000 - Register an OAuth2 Application: Inside Gitea, navigate to Site Administration > Applications (or User Settings > Applications). Create a new OAuth2 application named "Woodpecker CI".
- Set Redirect URI: Set the Redirect URI precisely to
[https://ci.example.com/authorize](https://ci.example.com/authorize). - Update Environment Variables: Copy the generated Client ID and Client Secret from Gitea, paste them into the
WOODPECKER_GITEA_CLIENTandWOODPECKER_GITEA_SECRETfields in yourdocker-compose.ymlfile, and restart the full stack usingdocker 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.
