Git Tags, Drone CI, and Watchtower — A Simple Deployment Pipeline
My CI/CD setup has changed a lot over the years. I started with Jenkins like most people, struggled with it, and eventually landed on something much simpler. Here’s how my current pipeline works.
The Jenkins Days
I used Jenkins for a while. It works, but maintaining it felt like a job on its own. Plugin updates breaking things, Groovy pipelines that nobody wants to debug, and a UI that feels like it’s from 2008. For a small team or solo developer, Jenkins is overkill.
I needed something lighter.
Git Tags as the Trigger
Before talking about the CI tool itself — the most important change I made was switching to git tags as my deployment trigger instead of pushing to a branch.
The flow is simple:
- Work on a feature branch
- Merge to main when ready
- Tag a release when I want to deploy
git tag v1.2.0
git push origin v1.2.0
This gives me full control over what gets deployed and when. Merging to main doesn’t automatically trigger a build — only a tag does. It also makes rollbacks straightforward since every deployment maps to a version.
Drone CI
I replaced Jenkins with Drone CI. It’s lightweight, runs in a single Docker container, and uses a simple YAML config. No plugins to manage, no Groovy scripts.
Here’s a simplified .drone.yml:
kind: pipeline
type: docker
name: build
trigger:
event:
- tag
ref:
- refs/tags/v*
steps:
- name: build-and-push
image: plugins/docker
settings:
registry: registry.example.com
repo: registry.example.com/team/myapp
username:
from_secret: docker_username
password:
from_secret: docker_password
tags:
- latest
- ${DRONE_TAG}
- ${DRONE_COMMIT_SHA:0:8}
cache_from:
- registry.example.com/team/myapp:latest
build_args:
- APP_VERSION=${DRONE_TAG}
A few things I like about this setup:
refs/tags/v*— only triggers on tags starting withv, so random tags don’t accidentally deployplugins/docker— handles the Docker build and push in one step, no need fordocker:dindor mounting the socket- Three tags —
latestfor Watchtower, the version tag for traceability, and the short commit SHA for debugging cache_from— pulls the previouslatestimage as cache so builds are fastbuild_args— passes the version into the build so the app knows what version it’s running- Secrets — registry credentials are stored in Drone’s secret manager, never in the YAML
When I push a git tag, Drone picks it up, builds the Docker image, tags it three ways, and pushes it to the registry. That’s it. If you want to speed up the Docker build step itself, I wrote about writing Dockerfiles that build fast.
Watchtower for Auto-Redeploy
Here’s where it gets nice. On the server, I run Watchtower — a container that watches for new Docker image versions and automatically restarts containers when a new image is available.
But I don’t want Watchtower to update every container. Some things I want to update manually. So I use the label-based approach — Watchtower only updates containers that have a specific label.
Run Watchtower with label filtering:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --label-enable --interval 60
Then on containers I want auto-updated, I add the label:
myapp:
image: myregistry/myapp:latest
labels:
- "com.centurylinklabs.watchtower.enable=true"
Containers without that label are left alone. This way, databases, reverse proxies, and other infrastructure stay untouched — only the app containers get redeployed automatically.
The Full Flow
- I push a git tag (
v1.2.0) - Drone CI builds the Docker image and pushes it to the registry as both
v1.2.0andlatest - Watchtower detects the new
latestimage within 60 seconds - Watchtower pulls the new image and restarts only the labeled containers
- Done — zero manual SSH, near zero downtime
The whole pipeline is lightweight. Drone runs in a single container, Watchtower runs in a single container. No complex orchestration, no Kubernetes, no managed CI service bills.
Key Takeaways
- Git tags give you explicit control over what gets deployed
- Drone CI is a great Jenkins replacement for small teams — simple YAML, no plugin hell
- Watchtower with
--label-enablelets you auto-deploy only the containers you choose - You don’t need a complex setup to deploy with confidence
If you need help setting up CI/CD for your project, I’m available for freelance work. Get in touch — I typically respond within 24 hours.
Related
Justfile vs Makefile — Which Task Runner Should You Use?
Comparing just and make as task runners for modern development workflows — syntax, features, and when each makes sense.
Migrating from Nginx to Caddy — Why I Switched and Never Looked Back
How Caddy replaced Nginx as my reverse proxy — automatic HTTPS, wildcard domains, and a config file that actually makes sense.
Nginx vs Caddy — Which Reverse Proxy Should You Use?
A practical comparison of Nginx and Caddy for web developers — automatic HTTPS, configuration, performance, and when to pick which.