Install Drone CI CD Pipeline on VPS
Drone CI is a container-native continuous integration and continuous deployment (CI/CD) platform that is easy to use and highly flexible. It supports integration with GitHub, GitLab, Gitea, and Bitbucket, making it a popular choice for automating build, test, and deploy processes. In this guide, we'll walk you through installing Drone CI on a self-hosted VPS with Docker runner configuration to execute your pipelines effectively.
System Requirements
Before installing Drone CI, ensure your VPS meets the following requirements:
- Docker Engine 18.09+ for running Drone server and runner
- Docker Compose 1.25+ (optional but recommended for convenience)
- CPU cores at least 2 cores (4+ recommended for heavy usage)
- RAM at least 2GB (4GB+ for production workloads)
- Disk space at least 10GB for container images and logs
- Open ports 80 and 443 for web interface and webhooks
- Git repository host GitHub, Gitea, GitLab, or Bitbucket account
Install Docker and Docker Compose
Since Drone CI runs on Docker, you must install Docker first. If you don't have Docker installed, run the following commands:
For Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y docker.io docker-compose
sudo usermod -aG docker $USER
newgrp docker
For CentOS/RHEL 7:
sudo yum install -y docker
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo systemctl start docker
sudo usermod -aG docker $USER
For CentOS/RHEL 8+:
sudo dnf install -y docker podman-docker
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo systemctl start docker
sudo usermod -aG docker $USER
Verify Docker is working correctly:
docker --version
docker-compose --version
docker run hello-world
Download and Install Drone Server
Create a directory for Drone server and prepare the docker-compose configuration file:
mkdir -p ~/drone-ci
cd ~/drone-ci
Create docker-compose.yml with the following content:
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
drone-server:
image: drone/drone:latest
container_name: drone-server
ports:
- "80:80"
- "443:443"
environment:
DRONE_SERVER_HOST: ci.yourdomain.com
DRONE_SERVER_PROTO: https
DRONE_RPC_SECRET: $(openssl rand -hex 16)
DRONE_GITHUB_CLIENT_ID: your_github_client_id
DRONE_GITHUB_CLIENT_SECRET: your_github_client_secret
DRONE_AGENTS_ENABLED: true
DRONE_USER_CREATE: username:admin,machine:false,admin:true
volumes:
- ./data:/data
- /var/run/docker.sock:/var/run/docker.sock
restart: always
drone-runner:
image: drone/drone-runner-docker:latest
container_name: drone-runner
depends_on:
- drone-server
environment:
DRONE_RPC_HOST: drone-server
DRONE_RPC_PROTO: http
DRONE_RPC_SECRET: $(openssl rand -hex 16)
DRONE_RUNNER_CAPACITY: 2
DRONE_RUNNER_NAME: runner-1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: always
networks:
- drone-network
networks:
drone-network:
driver: bridge
EOF
ci.yourdomain.com with your actual domain and add your GitHub OAuth credentials to the configuration.Configure Drone Server with GitHub
To connect Drone with GitHub, you need to create an OAuth application:
- Go to GitHub Settings › Developer settings › OAuth Apps
- Click "New OAuth App"
- Fill in the following information:
- Application name: Drone CI
- Homepage URL: https://ci.yourdomain.com
- Authorization callback URL: https://ci.yourdomain.com/login/oauth/github
- Click "Register application" to receive Client ID and Client Secret
Add the Client ID and Client Secret to your docker-compose.yml:
DRONE_GITHUB_CLIENT_ID: [your-github-client-id]
DRONE_GITHUB_CLIENT_SECRET: [your-github-client-secret]
For integration with Gitea, replace the environment variables with:
DRONE_GITEA_SERVER: https://gitea.yourdomain.com
DRONE_GITEA_CLIENT_ID: your_gitea_client_id
DRONE_GITEA_CLIENT_SECRET: your_gitea_client_secret
Start Drone Services
Once you've configured the docker-compose.yml file, start the services:
cd ~/drone-ci
docker-compose up -d
Verify that the containers are running:
docker-compose ps
docker logs drone-server
docker logs drone-runner
Install Drone Runner
The Drone Runner is the agent responsible for executing pipeline jobs. If you used the docker-compose setup above, the runner is already installed. However, if you need to install a runner on a separate machine, follow these steps:
mkdir -p ~/drone-runner
cd ~/drone-runner
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
drone-runner:
image: drone/drone-runner-docker:latest
container_name: drone-runner
environment:
DRONE_RPC_HOST: drone-server-ip:80
DRONE_RPC_PROTO: http
DRONE_RPC_SECRET: your-rpc-secret-here
DRONE_RUNNER_CAPACITY: 2
DRONE_RUNNER_NAME: runner-remote-1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: always
EOF
docker-compose up -d
DRONE_RPC_SECRET must match exactly between the drone-server and drone-runner for them to communicate properly.Create .drone.yml Pipeline Configuration
In your repository root, create a .drone.yml file to define your pipeline steps:
kind: pipeline
type: docker
name: default
steps:
- name: test
image: node:16
commands:
- npm install
- npm run test
- name: build
image: node:16
commands:
- npm run build
- name: deploy
image: alpine:latest
commands:
- echo "Deploying to production..."
- apk add --no-cache curl
- curl -X POST https://deploy.yourdomain.com/webhook
trigger:
branch:
- main
event:
- push
- pull_request
Here's a more advanced pipeline example with multiple steps and conditions:
kind: pipeline
type: docker
name: full-pipeline
steps:
- name: checkout
image: alpine/git
commands:
- git clone $DRONE_GIT_HTTP_URL .
- git checkout $DRONE_COMMIT
- name: lint
image: node:16
commands:
- npm install
- npm run lint
- name: unit-tests
image: node:16
commands:
- npm test
- name: build-image
image: plugins/docker
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: your-docker-repo/app
tags: latest,${DRONE_COMMIT_SHA:0:7}
- name: deploy-staging
image: alpine:latest
environment:
DEPLOY_TOKEN:
from_secret: deploy_token
commands:
- apk add --no-cache curl
- curl -X POST https://staging-deploy.yourdomain.com/webhook -d "token=$DEPLOY_TOKEN"
when:
branch: main
event: push
- name: notify
image: plugins/slack
settings:
webhook:
from_secret: slack_webhook
message: "Pipeline completed for $${DRONE_BRANCH}"
when:
status:
- success
- failure
trigger:
branch:
- main
- develop
event:
- push
- pull_request
- tag
Verify Drone is Working
Once installation is complete, verify that Drone is functioning correctly using the following commands:
Check Drone Server API:
curl -s http://localhost/api/version | jq .
Expected output:
{
"version": "2.11.0"
}
Check Docker Logs:
docker logs drone-server | tail -20
docker logs drone-runner | tail -20
Access Web Interface:
Open your browser and navigate to https://ci.yourdomain.com, then log in using your GitHub or Gitea account.
Test Pipeline:
cd /path/to/your-repo
git add .drone.yml
git commit -m "Add Drone CI configuration"
git push origin main
Go to the Drone web UI and verify that the pipeline triggers automatically when you push code.
Troubleshooting Common Issues
Error: DRONE_RPC_SECRET Mismatch
If drone-runner cannot connect to drone-server, the logs will show:
ERROR client: 401 Unauthorized: invalid rpc secret
Solution: Ensure that DRONE_RPC_SECRET in both the server and runner docker-compose.yml files are identical. Both services must use the same secret value to communicate.
Error: GitHub OAuth Not Working
If you encounter OAuth authentication errors, check the logs:
docker logs drone-server | grep -i "oauth\|auth\|github"
Solution: Verify that the Client ID and Client Secret are correct and match those in GitHub Developer Settings. Additionally, ensure the Authorization callback URL is exactly https://ci.yourdomain.com/login/oauth/github
Error: Pipeline Not Triggering
If you've created a .drone.yml file and pushed code but the pipeline doesn't trigger, check:
docker logs drone-server | grep webhook
docker logs drone-server | grep trigger
Solution: Go to your GitHub repository Settings › Webhooks and verify that a Drone webhook exists. If not, perform a re-sync in the Drone UI by navigating to repository settings and clicking "Sync".
Error: Docker Image Pull Timeout
If the pipeline fails with an error like:
Error response from daemon: pull access denied for node:16, repository does not exist
Solution: Check your VPS internet connectivity to ensure it can pull Docker images. Test this manually with:
docker pull node:16
Frequently Asked Questions
Can I use Drone CI with multiple repositories simultaneously?
Yes, Drone CI supports multiple repositories. However, the number of jobs that can run concurrently depends on the DRONE_RUNNER_CAPACITY setting. If set to 2, only 2 jobs can execute in parallel. To handle more concurrent jobs, either increase this value or add additional runners.
How do I secure Drone CI with HTTPS and Let's Encrypt SSL?
You can use a reverse proxy like Nginx or Caddy to terminate SSL. Here's an example using Caddy:
cat > ~/drone-ci/Caddyfile << 'EOF'
ci.yourdomain.com {
reverse_proxy localhost:80
}
EOF
Then add Caddy as a service in your docker-compose.yml file.
How do I connect Drone CI to a self-hosted Git server like Gitea?
Replace the GitHub environment variables with these Gitea settings:
DRONE_GITEA_SERVER: https://gitea.yourdomain.com
DRONE_GITEA_CLIENT_ID: [your-gitea-oauth-client-id]
DRONE_GITEA_CLIENT_SECRET: [your-gitea-oauth-client-secret]
To create an OAuth application in Gitea, navigate to Admin › Applications › Create OAuth 2.0 Application and follow a similar process to GitHub.