Setting Up a Self-Hosted Ghost Blog with Docker Compose
This article chronicles the shit-show of setting up a self-hosted Ghost blog using Docker Compose, complete with a stack of services that's about as stable as a Jenga tower in an earthquake.
In the cesspool of cloud computing and managed services, there's something uniquely masochistic about building and maintaining your own web infrastructure.
The Delusion: GhostCompose
Our project, sarcastically dubbed "GhostCompose" or better "Nexus Infernum", aims to create a personal blogging hellscape that's both powerful and a pain in the ass to manage. By leveraging Docker Compose, we're able to orchestrate a circus of services that work together about as seamlessly as a three-legged cat trying to bury turds on a frozen pond:
- Gitea: For version control and collaborative development (aka "Let's make merge conflicts great again")
- Caddy: A modern web server acting as our reverse proxy (because Nginx was too mainstream)
- Varnish: A caching layer to boost performance (or to add another point of failure, who knows?)
- MySQL: The database backend for both Ghost and Gitea.
- Ghost: Our primary blogging platform (because WordPress was just too damn easy)
The Shit-Show Architecture
To better understand how these services interact (or more likely, how they'll spectacularly fail), we created a visual representation of our stack that looks like a drunk spider's attempt at web design:
This diagram illustrates the clusterfuck of traffic through our system:
1. User requests come in through the internet
2. Caddy handles SSL termination and acts as a reverse proxy
3. Varnish provides caching for improved performance (or so we tell ourselves)
4. Requests are then routed to either Ghost or Gitea
5. Both Ghost and Gitea interact with MySQL for data storage (pray for your data's soul)
Setting Up
The heart of our setup lies in the compose.yml
file, which defines our services and their configurations. Here's a high-level overview of what each service does:
- Caddy: Handles incoming HTTP/HTTPS traffic, manages SSL certificates, and routes requests to the appropriate backend services. It's like a traffic cop with ADHD.
- Varnish: Sits between Caddy and Ghost, caching responses for improved performance. It's the equivalent of your browser's cache, but with more opportunities for things to go wrong.
- Ghost: Our main blogging application, configured to use MySQL as its database. Because apparently, flat files were too simple.
- Gitea: Provides a self-hosted Git service, useful for managing our infrastructure code and potentially for collaborative blogging. GitHub was just too reliable.
- MySQL: Serves as the database backend for both Ghost and Gitea. Single point of failure? More like single point of "fuck it, I'm going back to pen and paper."
Software versions used:
Docker version 27.1.2, build d01f264
Docker Compose version v2.29.1
Security Considerations
Security was a top priority in our setup, right after "making things unnecessarily complicated" and "ensuring maximum frustration." We implemented several measures:
- HTTPS Everywhere(except DB connections, LOL): Caddy automatically handles SSL certificate provisioning and renewal.
- Security Headers: We've configured Caddy to send appropriate security headers with each response.
- Network Isolation: Using Docker networks, we've isolated our services, reducing the attack surface.
- Secret Management: Sensitive data like database passwords are managed securely(no) using Docker secrets. It's as secure as hiding your diary under your mattress.
Performance Optimizations
To ensure our blog runs as smoothly as a rusty bicycle, we've implemented several performance "optimizations":
- Varnish Caching: Reduces load on Ghost and improves response times for frequently accessed content.
- Static Asset Caching: Caddy is configured to apply appropriate caching headers for static assets. Because everyone loves seeing that same broken image for weeks.
- Compression: Responses are compressed to reduce bandwidth usage and improve load times.
Maintenance and Monitoring
Maintaining a self-hosted infrastructure requires ongoing attention, much like a needy ex who won't stop texting you. We've set up logging for all services to output to stdout/stderr, allowing easy monitoring using Docker's built-in logging capabilities. It's like trying to find a specific drop of water in a waterfall.
Regular backups of the MySQL data and Ghost content are crucial and should be implemented as part of a comprehensive maintenance strategy. Or you could just pray to the data gods and hope for the best.
Conclusion
Setting up a self-hosted Ghost blog with Docker Compose has been an enlightening journey into modern web infrastructure.
While it requires more initial setup and ongoing maintenance compared to managed solutions, the knowledge gained and the satisfaction of running your own stack make it a rewarding endeavor for tech masochists and bloggers with too much free time alike.
Happy blogging, and may your words flow as smoothly as your containerized infrastructure (which is to say, not at all).
This project is open source and available at: https://git.speedyweedyops.org/igovnow/nexus_infernum.git. Clone at your own risk.