CI/CD Platform

    Self-Hosted Drone CI

    Deploy Drone CI, the container-native continuous integration platform, on RamNode VPS. Automate your build, test, and deployment workflows with Docker-based pipelines.

    Ubuntu 22.04/24.04 LTS
    Docker Required
    ⏱️ 30-45 minutes

    Why Drone CI?

    Drone CI is a modern, container-native continuous integration and continuous delivery (CI/CD) platform. Built on Docker, it provides a lightweight, scalable solution for automating your build, test, and deployment workflows.

    Container-Native: Isolated Docker builds
    Lightweight: Low resource footprint
    Simple YAML-based pipelines
    Scalable runner architecture
    Multi-platform support
    Git provider integrations

    Introduction

    Architecture Overview

    Drone consists of two main components:

    • Drone Server: Handles webhooks from your Git provider, manages the web UI, and coordinates pipeline execution.
    • Drone Runner: Executes the actual pipeline steps. Runners poll the server for work and can be deployed on the same or separate machines.

    Note: This guide covers deploying Drone with the Docker runner, which executes pipeline steps inside Docker containers.

    Prerequisites

    VPS Requirements

    • • OS: Ubuntu 22.04/24.04 LTS
    • • RAM: 2 GB minimum (4 GB recommended)
    • • Storage: 20 GB SSD
    • • CPU: 2 vCPUs minimum

    Network Requirements

    • • Domain name (e.g., ci.yourdomain.com)
    • • Open ports: 80 (HTTP), 443 (HTTPS)
    • • Static IPv4 address
    • • SSH access to server

    Git Provider Account

    Drone integrates with various Git providers. You will need an account with one of:

    GitHub
    GitHub Enterprise
    GitLab
    GitLab Self-Hosted
    Gitea
    Gogs
    Bitbucket

    Need to set up a self-hosted Git provider? See our guides for GitLab and Gitea.

    Server Preparation

    Step 1: Update System Packages

    Connect to your RamNode VPS via SSH and update the system:

    Update System
    sudo apt update && sudo apt upgrade -y

    Step 2: Set System Hostname

    Set Hostname
    sudo hostnamectl set-hostname ci.yourdomain.com

    Step 3: Configure Firewall

    Install and configure UFW:

    Configure UFW
    sudo apt install ufw -y
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow ssh
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable

    Step 4: Create Dedicated User (Optional)

    For security, create a dedicated user for running Drone:

    Create User
    sudo adduser drone
    sudo usermod -aG docker drone

    Note: Add the user to the docker group after installing Docker in the next section.

    Installing Docker

    Drone requires Docker to run both the server and execute pipeline containers.

    Step 1: Install Docker Engine

    Install Docker
    # Install prerequisites
    sudo apt install ca-certificates curl gnupg -y
    
    # Add Docker GPG key
    sudo install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
    sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    sudo chmod a+r /etc/apt/keyrings/docker.gpg
    
    # Add Docker repository
    echo "deb [arch=$(dpkg --print-architecture) \
    signed-by=/etc/apt/keyrings/docker.gpg] \
    https://download.docker.com/linux/ubuntu \
    $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    # Install Docker
    sudo apt update
    sudo apt install docker-ce docker-ce-cli containerd.io \
    docker-buildx-plugin docker-compose-plugin -y

    Step 2: Configure Non-Root Access

    Add User to Docker Group
    sudo usermod -aG docker $USER
    newgrp docker

    Step 3: Verify Installation

    Verify Docker
    docker --version
    docker run hello-world

    Git Provider Integration

    Drone requires OAuth credentials from your Git provider to authenticate users and receive webhooks.

    GitHub Integration

    Step 1: Create OAuth Application

    Navigate to GitHub Settings → Developer settings → OAuth Apps → New OAuth App

    • Application name: Drone CI
    • Homepage URL: https://ci.yourdomain.com
    • Authorization callback URL: https://ci.yourdomain.com/login

    Step 2: Save Credentials

    Note the Client ID and generate a Client Secret.

    Warning: Store credentials securely. The client secret is only shown once.

    GitLab Integration

    Navigate to User Settings → Applications → Add new application

    • Name: Drone CI
    • Redirect URI: https://ci.yourdomain.com/login
    • Scopes: Select api and read_user

    Gitea Integration

    Navigate to Settings → Applications → Create a new OAuth2 Application

    Redirect URI: https://ci.yourdomain.com/login

    Deploying Drone Server

    Step 1: Create Directory Structure

    Create Directories
    sudo mkdir -p /opt/drone
    sudo mkdir -p /opt/drone/data
    cd /opt/drone

    Step 2: Generate Shared Secret

    Generate a shared secret for communication between the server and runners:

    Generate Secret
    openssl rand -hex 16

    Save this value for use in both server and runner configurations.

    Step 3: Create Docker Compose File

    /opt/drone/docker-compose.yml
    # /opt/drone/docker-compose.yml
    version: '3.8'
    
    services:
      drone-server:
        image: drone/drone:2
        container_name: drone-server
        restart: always
        ports:
          - '8080:80'
        volumes:
          - ./data:/data
        environment:
          # Server Configuration
          - DRONE_SERVER_HOST=ci.yourdomain.com
          - DRONE_SERVER_PROTO=https
          - DRONE_DATABASE_DRIVER=sqlite3
          - DRONE_DATABASE_DATASOURCE=/data/database.sqlite
    
          # GitHub Configuration (use GitLab vars for GitLab)
          - DRONE_GITHUB_CLIENT_ID=your_client_id
          - DRONE_GITHUB_CLIENT_SECRET=your_client_secret
    
          # Security
          - DRONE_RPC_SECRET=your_shared_secret_here
          - DRONE_USER_CREATE=username:yourgithubusername,admin:true
    
          # Logging
          - DRONE_LOGS_DEBUG=false
          - DRONE_LOGS_TRACE=false

    Important: Replace placeholder values with your actual OAuth credentials and shared secret.

    GitLab Configuration Variant

    For GitLab, use these environment variables instead:

    GitLab Configuration
    - DRONE_GITLAB_SERVER=https://gitlab.com
    - DRONE_GITLAB_CLIENT_ID=your_application_id
    - DRONE_GITLAB_CLIENT_SECRET=your_secret

    Step 4: Start Drone Server

    Start Server
    docker compose up -d drone-server
    docker compose logs -f drone-server

    Deploying Drone Runner

    The Docker runner executes pipeline steps inside containers.

    Step 1: Add Runner to Docker Compose

    Append the runner service to your docker-compose.yml:

    Runner Configuration
      drone-runner:
        image: drone/drone-runner-docker:1
        container_name: drone-runner
        restart: always
        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=your_shared_secret_here
          - DRONE_RUNNER_CAPACITY=2
          - DRONE_RUNNER_NAME=drone-runner-1
          - DRONE_RUNNER_NETWORKS=drone_default

    Configuration Options

    • DRONE_RUNNER_CAPACITY – Number of concurrent pipelines (default: 2)
    • DRONE_RUNNER_NAME – Unique identifier for this runner
    • DRONE_RPC_SECRET – Must match server's RPC secret

    Step 2: Start the Runner

    Start Runner
    docker compose up -d
    docker compose logs -f drone-runner

    Nginx Reverse Proxy with SSL

    Step 1: Install Nginx and Certbot

    Install Packages
    sudo apt install nginx certbot python3-certbot-nginx -y

    Step 2: Create Nginx Configuration

    /etc/nginx/sites-available/drone
    # /etc/nginx/sites-available/drone
    server {
        listen 80;
        server_name ci.yourdomain.com;
    
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # WebSocket support for real-time logs
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_cache_bypass $http_upgrade;
    
            # Timeouts for long-running builds
            proxy_connect_timeout 300;
            proxy_send_timeout 300;
            proxy_read_timeout 300;
        }
    }

    Step 3: Enable Site and Obtain SSL

    Enable Site and SSL
    sudo ln -s /etc/nginx/sites-available/drone /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    
    # Obtain SSL certificate
    sudo certbot --nginx -d ci.yourdomain.com

    Step 4: Verify SSL Auto-Renewal

    Test Renewal
    sudo certbot renew --dry-run

    Creating Your First Pipeline

    Drone pipelines are defined in a .drone.yml file at the root of your repository.

    Basic Pipeline Example

    .drone.yml
    kind: pipeline
    type: docker
    name: default
    
    steps:
      - name: test
        image: node:20-alpine
        commands:
          - npm ci
          - npm test
    
      - name: build
        image: node:20-alpine
        commands:
          - npm run build
        depends_on:
          - test

    Pipeline with Secrets

    Store sensitive values as secrets in the Drone UI, then reference them:

    Using Secrets
    steps:
      - name: deploy
        image: alpine
        environment:
          SSH_KEY:
            from_secret: deploy_ssh_key
        commands:
          - echo "$SSH_KEY" > /tmp/key
          - chmod 600 /tmp/key
          - ssh -i /tmp/key user@server 'deploy.sh'

    Conditional Execution

    Run steps only on specific branches or events:

    Conditional Steps
    steps:
      - name: deploy-production
        image: alpine
        commands:
          - ./deploy.sh production
        when:
          branch:
            - main
          event:
            - push

    Activate Repository

    1. Log in to Drone at https://ci.yourdomain.com
    2. Click "Sync" to fetch your repositories
    3. Find your repository and click "Activate"
    4. Push a commit or create a pull request to trigger your first build

    Security Best Practices

    Restrict Admin Access

    Limit admin users by setting DRONE_USER_CREATE:

    Admin Restriction
    DRONE_USER_CREATE=username:admin_user,admin:true

    Enable Repository Filtering

    Restrict which organizations or users can activate repositories:

    User Filtering
    DRONE_USER_FILTER=your-org,trusted-user

    Secrets Management

    • Use Drone's built-in secrets: Never commit sensitive values to .drone.yml
    • Limit secret scope: Restrict secrets to specific repositories or events
    • Rotate secrets regularly: Update OAuth credentials and RPC secrets periodically

    Resource Limits

    Prevent resource exhaustion by adding limits:

    Resource Limits
    services:
      drone-runner:
        deploy:
          resources:
            limits:
              cpus: '2'
              memory: 2G

    Maintenance and Troubleshooting

    Common Issues

    Runner Not Connecting

    Verify the RPC secret matches between server and runner:

    Check Secrets
    docker compose logs drone-server | grep -i secret
    docker compose logs drone-runner | grep -i rpc

    OAuth Callback Errors

    Ensure your callback URL exactly matches your Git provider configuration. Check for missing trailing slashes, incorrect protocol, or typos.

    Webhooks Not Triggering

    Check webhook delivery in your Git provider's settings. Verify the webhook URL is reachable and the repository is activated.

    Useful Commands

    Management Commands
    # View all logs
    docker compose logs -f
    
    # Restart services
    docker compose restart
    
    # Update to latest version
    docker compose pull
    docker compose up -d
    
    # Check container status
    docker compose ps
    
    # Access Drone server shell
    docker compose exec drone-server sh

    Backup Database

    Backup Database
    # Stop server before backup
    docker compose stop drone-server
    
    # Create backup
    cp /opt/drone/data/database.sqlite /backup/drone-$(date +%Y%m%d).sqlite
    
    # Restart server
    docker compose start drone-server

    Log Rotation

    Configure log rotation to prevent disk space issues:

    Log Rotation
    services:
      drone-server:
        logging:
          driver: 'json-file'
          options:
            max-size: '10m'
            max-file: '3'

    Deployment Complete!

    Your Drone CI installation is now ready. Access the web interface at your configured domain, authenticate with your Git provider, and start automating your builds!

    Ready to automate your builds?

    Get started with a RamNode VPS and deploy Drone CI in minutes.