Homelab CI/CD Pipelines
Homelab CI/CD Pipelines
For professional DevOps engineers, the "Cloud" is just someone else's computer. To truly understand the infrastructure layer, there is no better playground than a Homelab. Running your own private cloud forces you to deal with networking, storage, and orchestration without the safety net of AWS managed services.
In this guide, I'll walkthrough how I automated the deployment of my personal services using a GitOps approach with Jenkins and Docker, running entirely on bare metal at home.
The Hardware Stack
My setup aims for low power and silence:
- Compute: 3x Intel NUCs (i5, 64GB RAM) running Proxmox VE.
- Storage: Synology NAS (NFS for persistent volumes).
- Network: Unifi Dream Machine Pro + 10Gbps Uplink.
The Problem: "ssh and git pull"
Initially, updating a service meant SSH-ing into the server, running git pull, and docker compose up -d. This is manual, error-prone, and doesn't scale. We want Continuous Deployment.
The Solution: Jenkins + Webhooks
Reference architecture:
- Code: Source code lives in a self-hosted Gitea instance (or GitHub).
- Trigger: Pushing to the
mainbranch sends a Webhook to Jenkins. - Build: Jenkins spins up an ephemeral Docker agent.
- Packaging: Steps:
- Build Docker Image.
- Run Unit Tests.
- Push Image to local Registry (Harbor).
- Deploy: SSH into the production node and update the service.
Jenkins Pipeline (Jenkinsfile)
Using a declarative pipeline ensures our build process is version controlled.
pipeline {
agent { docker { image 'node:20-alpine' } }
environment {
REGISTRY = 'registry.local:5000'
IMAGE_NAME = 'my-dashboard'
}
stages {
stage('Build') {
steps {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Dockerize') {
steps {
script {
def app = docker.build("${REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER}")
docker.withRegistry('http://registry.local:5000') {
app.push()
app.push('latest')
}
}
}
}
stage('Deploy') {
steps {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'Production-Node',
transfers: [
sshTransfer(
execCommand: "docker service update --image ${REGISTRY}/${IMAGE_NAME}:${env.BUILD_NUMBER} my_stack_dashboard"
)
]
)
]
)
}
}
}
}Why Self-Hosted?
Why go through this trouble instead of using Vercel or Netlify?
- Data Sovereignty: My photos, documents, and code never leave my house.
- Cost: 64GB of RAM on AWS costs hundreds a month. At home, it's a one-time purchase.
- Latency: accessing services over 10Gbps LAN is instant.
- Learning: You learn why Docker Swarm networking fails, or how DNS resolution works in Kubernetes, because you have to fix it yourself.
Conclusion
Building a CI/CD pipeline for a homelab may seem like overkill, but it brings professional discipline to personal projects. It ensures that your private cloud is as reliable and automated as the ones you build at work.