Stop Wasting RAM: Building a Lightweight CI/CD Pipeline with Drone and Gitea

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

The Bloated CI/CD Problem

I once tried running Jenkins on a $5-a-month DigitalOcean droplet to manage a small Python project. It was a disaster. Within minutes, the Linux OOM (Out of Memory) killer terminated the process because Jenkins was idling at nearly 1.5GB of RAM. For independent developers or small teams, traditional CI/CD tools often feel like using a sledgehammer to crack a nut.

Most enterprise tools are designed for massive clusters, not lean VPS environments. If you are self-hosting a Git server like Gitea, you likely want a pipeline that matches its efficiency. You need a system that stays out of the way, consumes fewer than 100MB of RAM at rest, and only consumes CPU cycles when it is actually building code.

This is where Drone CI shines. It is built entirely on a container-first architecture. Every step in your pipeline is simply a Docker container that spins up, does its job, and disappears. When paired with Gitea, you get a GitHub-like experience on a server with as little as 1GB of total RAM.

Why Drone CI and Gitea?

Gitea is a lightweight Git service written in Go. It is fast enough to run on a Raspberry Pi while providing a polished UI. Drone CI shares this philosophy. Instead of a complex web of plugins, Drone uses a clean .drone.yml file located right in your repository.

This “Pipeline as Code” model ensures your build logic evolves alongside your software. Since Drone is container-native, you never have to manually install Node.js, Python, or Go on your host server. If your project needs Node 20, you just pull the node:20 image for that specific step. This isolation prevents the “it works on my machine” syndrome and keeps your host OS clean.

Mastering this stack provides a massive advantage. You learn the core principles of container orchestration and environment isolation without the steep learning curve of Kubernetes.

Setting Up Your Lightweight Stack

We will deploy both Gitea and Drone CI on a single server using Docker Compose. Using a shared bridge network allows these containers to communicate securely without exposing every port to the public internet.

Step 1: System Requirements

A basic Ubuntu 22.04 server with 1GB of RAM and 1 CPU core is sufficient for this setup. Ensure you have Docker and the Compose plugin installed. You will also need a domain name (e.g., ci.yourdomain.com) to handle OAuth callbacks correctly.

Step 2: The Docker Compose Configuration

Create a dedicated directory and initialize your configuration file:

mkdir devops-stack && cd devops-stack
touch docker-compose.yml

Insert the following configuration. This setup defines the Gitea service, the Drone Server (the brain), and the Drone Runner (the muscle).

version: '3.8'

services:
  gitea:
    image: gitea/gitea:1.21
    container_name: gitea
    restart: always
    networks:
      - ci-network
    ports:
      - "3000:3000"
      - "222:22"
    volumes:
      - ./gitea:/data

  drone-server:
    image: drone/drone:2
    container_name: drone-server
    ports:
      - "8080:80"
    volumes:
      - ./drone:/data
    restart: always
    networks:
      - ci-network
    environment:
      - DRONE_GITEA_CLIENT_ID=${GITEA_CLIENT_ID}
      - DRONE_GITEA_CLIENT_SECRET=${GITEA_CLIENT_SECRET}
      - DRONE_GITEA_SERVER=http://gitea:3000
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
      - DRONE_SERVER_HOST=ci.yourdomain.com
      - DRONE_SERVER_PROTO=http

  drone-runner:
    image: drone/drone-runner-docker:1
    container_name: drone-runner
    restart: always
    networks:
      - ci-network
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_RPC_PROTO=http
      - DRONE_RPC_HOST=drone-server
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
      - DRONE_RUNNER_CAPACITY=2
      - DRONE_RUNNER_NAME=main-runner

networks:
  ci-network:
    driver: bridge

Step 3: Connecting Gitea to Drone

Drone doesn’t have its own user database; it relies on Gitea for authentication. First, launch Gitea:

docker-compose up -d gitea
  1. Navigate to http://your-ip:3000 and complete the initial setup.
  2. Log in and navigate to Settings -> Applications -> OAuth2 Applications.
  3. Create a new application named “Drone CI”.
  4. Set the Redirect URI to http://ci.yourdomain.com/login.
  5. Save the Client ID and Client Secret immediately.

Step 4: Securing the Secrets

Create a .env file. This keeps your sensitive credentials out of the main compose file. Use a strong random string for the RPC secret to ensure the runner and server can communicate safely.

GITEA_CLIENT_ID=your_id_here
GITEA_CLIENT_SECRET=your_secret_here
DRONE_RPC_SECRET=$(openssl rand -hex 16)

Step 5: Deployment

Launch the full stack with a single command:

docker-compose up -d

Visit http://ci.yourdomain.com. Drone will redirect you to Gitea for authorization. Once you click “Authorize,” your Gitea repositories will appear in the Drone dashboard, ready to be activated.

Step 6: Creating Your First Pipeline

To test the system, add a .drone.yml file to the root of any repository. Here is a practical example for a Node.js application:

kind: pipeline
type: docker
name: test-and-deploy

steps:
- name: install-dependencies
  image: node:20-slim
  commands:
  - npm install
  - npm test

- name: notify-success
  image: plugins/webhook
  settings:
    urls: https://discord.com/api/webhooks/your-id
    content: "Build successful!"

The moment you push this code, Gitea sends a webhook to Drone. Drone then pulls the node:20-slim image, executes your tests, and cleans up after itself. Total resource usage during this build? Usually under 200MB of RAM.

Summary

Self-hosting your CI/CD doesn’t have to be expensive or resource-heavy. By combining Docker, Drone, and Gitea, you have built a professional-grade pipeline that is private, fast, and remarkably efficient. You are no longer tethered to cloud provider credits or bloated Java applications.

To take this further, consider adding a reverse proxy like Caddy or Nginx with Let’s Encrypt. This will provide HTTPS and allow you to run everything on standard port 443. Once you master the YAML syntax, you can explore advanced features like parallel build steps or automated Docker image uploads to a private registry.

Share: