It’s the site you’re on!
I enjoy homelabbing to have a nice sandbox to learn about programming. I wanted to learn about Continuous Integration/Continuous Deployment (CI/CD) pipelines, so I thought a pipeline that used a static-site generator (SSG) in a Docker container would be a fun project. The site is built around Hugo, an SSG that converts markdown files into the content you see, which builds on my homelab server and is then pushed to a Digital Ocean Container Registry as the last part of my CI/CD pipeline.
The CI/CD pipeline means that I can constantly serve this portfolio. I can tweak content and preview my feature brancehs on my local network, and then serve the latest image automatically when successful builds merge to main. The entire infrastructure runs on self-hosted GitLab Community Edition, giving me complete control over my development workflow while learning modern DevOps practices. What makes this project interesting is that it demonstrates enterprise-grade automation techniques in a personal homelab environment, showcasing how modern software deployment practices can be applied to even small-scale projects.
The Problem It Solves
Before this pipeline, updating my website meant manually running build commands, copying files to a server, and hoping nothing broke. Now, I simply write content in markdown, commit it to my Gitlab instance, and the entire process happens automatically. This not only saves time but ensures every deployment is consistent, traceable, and reversible.
How It Works
The Workflow
When I push a commit to GitLab, the pipeline springs into action:
- Build Stage: Hugo converts all markdown files into HTML, CSS, and JavaScript
- Containerization: The static files are packaged into a minimal Nginx container
- Deployment: Docker Compose automatically updates the running website with zero downtime
The entire process takes about 2-3 minutes from commit to live website, and I can watch it happen in real-time through GitLab’s pipeline interface.
Architecture
┌─────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
│ GitLab │ --> │ Builder │ --> │ Runtime │ --> │ Nginx │
│ CE │ │ (Hugo) │ │ (Docker) │ │ Server │
│ Repository │ │ │ │ │ │ Port 3030 │
└─────────────┘ └──────────────┘ └─────────────┘ └─────────────┘
│ │ │ │
│ Push commit │ Generate HTML │ Package files │ Serve site
└─────────────────────┴─────────────────────┴───────────────────┘
Multi-Stage Docker Builds
The pipeline uses Docker’s multi-stage build feature to create optimized containers. The first stage installs Hugo and all build dependencies, then generates the static site. The second stage takes only the final HTML files and packages them with a minimal Nginx server—resulting in a container that’s just 25MB instead of hundreds of megabytes.
This approach means the final production container doesn’t include Hugo, Go, or any build tools—only the web server and the static files it needs to serve.
Key Features
Automated CI/CD Pipeline
The GitLab CI/CD pipeline handles everything automatically:
- Build: Compiles Hugo site from markdown source files
- Deploy: Updates the running container with zero downtime
- Versioning: Each deployment is tagged with the commit hash for easy rollback
Self-Hosted Infrastructure
Everything runs on my own hardware using GitLab Community Edition. This provides:
- Complete control over my development workflow
- No dependency on external SaaS services
- Learning opportunity to understand enterprise DevOps tools
- Cost-effective solution for personal projects
- Learning opportunity to understand runners + hardware including tags and asset management
Rollback Capabilities
If something goes wrong, I can instantly revert to any previous version using the rollback script. Each deployment is tagged with its Git commit hash, making it easy to identify and restore previous working versions.
Technical Stack
- Hugo 0.152.2: Static site generator that converts markdown to HTML
- GitLab CE: Self-hosted version control and CI/CD platform
- Docker: Containerization for reproducible builds and deployments
- Docker Compose: Orchestration for easy service management
- Nginx (Alpine): Lightweight web server for serving static content
- Bash: Deployment and rollback scripts
Pipeline Stages
The CI/CD pipeline defined in .gitlab-ci.yml consists of two main stages:
Build Stage
- Creates a Docker container with Hugo installed
- Runs
hugo --minifyto generate optimized static files - Extracts the built files as artifacts for the next stage
Deploy Stage
- Builds a minimal Nginx container using the pre-built static files
- Tags the image with the commit hash for version tracking
- Uses Docker Compose to update the running service without downtime
Lessons Learned
Building this pipeline taught me valuable lessons about modern DevOps practices:
- How containerization makes deployments predictable and reproducible
- The power of multi-stage builds for creating minimal production images
- How CI/CD pipelines can automate repetitive tasks and reduce human error
- Managing runners, asset access, and debugging across systems
This site has been a fun way to learn about how so much of the modern internet is presented to consumers. In learning these DevOps practices, I have a new tool for creating software that needs high uptime and reliability.