Moving to Docker
The first version of my setup had Ghost and MySQL installed directly on the
system. It worked, but it was fragile. One bad command and everything breaks.
Reinstalling takes hours.
Docker solves this. Instead of installing software directly on the server,
everything runs in isolated containers. If something breaks, you restart the
container. If you want to move the entire stack to another machine, you copy
one file.
The concept is simple:
- Image: the blueprint of a service
- Container: a running instance of that blueprint
- Volume: persistent storage that survives container restarts
- Docker Compose: a file that defines and orchestrates multiple containers together
Containerizing Ghost and MySQL
I created a docker-compose.yml file that defines both services:
Ghost needs MySQL to store posts, users and settings. The challenge is that
Ghost tries to connect to MySQL immediately on startup — but MySQL takes time
to initialize. The solution is a healthcheck: Ghost waits until MySQL is
confirmed healthy before attempting to start.
This single file replaces everything I manually installed yesterday. To bring
the entire stack up:
docker compose up -d
To bring it down:
docker compose down
That's it. The entire blog infrastructure in two commands.
One important lesson: never run docker compose down -v unless you want to
delete your data. The -v flag removes volumes — where your database lives.
I learned this the hard way.
Adding n8n
n8n is a workflow automation platform — think of it as a self-hosted version
of Zapier or Make, but running entirely on your own hardware.
I added it as a second Docker Compose stack, intentionally kept local-only.
n8n has access to credentials, API keys and internal automations — not
something you want exposed to the public internet.
To access it I use the local IP of the server from any device on my home network:
No Cloudflare tunnel. No public exposure. Just local access on my network.
Building a Self-Healing Server Agent
This is where it got interesting.
I built an automated monitoring system using n8n that:
- Runs every 5 minutes
- Collects server metrics via SSH: CPU, RAM, disk, temperature, battery,
Docker container status and HTTP response from leobunker.dev - Processes the metrics and checks against thresholds
- If something is wrong, triggers a Claude AI agent
- The agent analyzes the problem, executes the appropriate fix via SSH,
and sends a Telegram notification with a full incident report
The agent knows the server context — where Docker Compose files live, how
Ghost depends on MySQL, which containers should be running. It does not just
restart containers blindly. It understands the dependency chain.
When Ghost went down during testing, the agent detected the 502 error,
executed the correct recovery sequence and reported back — all without
manual intervention.

What I Learned
- Docker Compose healthchecks are not optional when services depend on each other
- Never expose automation tools like n8n to the public internet
- An AI agent is only as good as the context you give it
- Published vs draft workflows in n8n are not the same thing — always publish before running
- Building something that monitors and fixes itself feels very different from
building something that just runs
Current Stack
- Ghost + MySQL running in Docker
- n8n running in Docker, local-only
- Automated monitoring via n8n + Claude AI
- Telegram notifications for server incidents
- Cloudflare Tunnel routing traffic to Ghost on port 3001
What's Next
Kubernetes. But first, a second machine.
— Leo RH.