Setting Up a CI/CD Pipeline with Drone CI and Harbor
Omobayonle Ogundele
MAIN_NODE: DEVOPS_ENGINEER
Getting your code from your laptop to production automatically is one of the most satisfying things you can set up as a DevOps engineer. In this post I'll walk you through how I set up a fully automated CI/CD pipeline using Drone CI and Harbor — the same setup powering this very website.
What we're building
By the end of this post you'll have a pipeline that:
- Automatically triggers when you push code to Gitea
- Builds a Docker image of your app
- Pushes it to Harbor (your private Docker registry)
- SSHs into your server and deploys the new image
What you need
- A Linux server (I'm using Oracle Cloud)
- Docker installed
- Gitea running
- Drone CI running and connected to Gitea
- Harbor running as your private registry
Step 1 — Set up Harbor
Harbor is an open source container registry. Think of it like a private Docker Hub that you host yourself. To run it on your server pull the Harbor installer from the official GitHub releases page, extract it and run the install script.
Once it's running you can access it at your server IP on port 80. Create a project called library — this is where your images will live.
Step 2 — Connect Drone to Gitea
Drone CI watches your Gitea repositories for pushes. When you push code it reads your .drone.yml file and runs the pipeline automatically. In your Gitea settings create an OAuth application for Drone, then configure Drone with the client ID and secret. Once connected every repository you activate in Drone will trigger builds on push.
Step 3 — Write your Dockerfile
Your app needs a Dockerfile so Drone can build it into an image. Here's the one I use for my Flask portfolio:
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "run:app"]
Keep it simple. The slim base image keeps the final image small and fast to pull.
Step 4 — Write your .drone.yml
This is the heart of your pipeline. It tells Drone exactly what to do when you push code:
kind: pipeline
type: docker
name: deploy
clone:
disable: true
steps:
- name: clone-code
image: alpine/git
commands:
- git clone http://your-gitea-ip:3001/username/repo.git .
- git checkout ${DRONE_COMMIT}
- name: build-and-push
image: plugins/docker
settings:
registry: your-harbor-ip
repo: your-harbor-ip/library/portfolio
tags:
- latest
- build-${DRONE_BUILD_NUMBER}
username: admin
password:
from_secret: harbor_password
insecure: true
- name: deploy-to-server
image: appleboy/drone-ssh
settings:
host: your-server-ip
username: ubuntu
key:
from_secret: ssh_key
port: 22
script:
- docker login -u admin -p yourpassword your-harbor-ip
- docker pull your-harbor-ip/library/portfolio:latest
- docker stop portfolio || true
- docker rm portfolio || true
- docker volume create portfolio_data || true
- docker run -d
--name portfolio
-p 80:5000
--restart unless-stopped
-v portfolio_data:/app/instance
your-harbor-ip/library/portfolio:latest
Three steps — clone, build and push, deploy. Clean and simple.
Step 5 — Add your secrets in Drone
Never put passwords in your .drone.yml file. Instead go to your Drone dashboard, open your repository settings and add secrets:
harbor_password— your Harbor passwordssh_key— your server private SSH key
Drone injects these at build time so they never appear in your code.
Step 6 — Push and watch it deploy
Now just push your code:
git add . git commit -m "my first automated deployment" git push
Open your Drone dashboard and watch the pipeline run in real time. You'll see each step execute — clone, build, push, deploy. When it turns green your code is live on your server automatically.
The result
Every time I push to my main branch my site updates itself within about 2 minutes. No manual SSH, no manual Docker commands, no risk of forgetting a step. Just push and it's done.
This is the power of CI/CD — it removes the human error from deployments and lets you ship with confidence.
What's next
From here you can extend the pipeline to add automated tests before the build step, send Slack or email notifications when deployments succeed or fail, set up staging and production environments, and add health checks after deployment.
CI/CD is one of those things that feels complex at first but once it's running you'll never want to go back to manual deployments.
Omobayonle Ogundele
DevOps Engineer based in Lagos, Nigeria. Building reliable infrastructure and sharing logs from the edge of production.
Comments (0)
No comments yet. Be the first!