BACK TO ENTRIES
LOG_ENTRY: 2026.03.08 · 9 TELEMETRY_HITS

Setting Up a CI/CD Pipeline with Drone CI and Harbor

B

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 password
  • ssh_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.

B

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!

LEAVE_RESPONSE