Setting up a Blog Server under Docker
This is part of a series describing how I've set up and maintain the Ghost blogging service in my Docker stack.
With the Docker environment up and running, the first step is to create the directory structure:
mkdir -p ~/docker/ghost
cd ~/docker/ghostThen we need a Compose file:
nano docker-compose.ymlInto the empty compose file we include:
services:
ghost-db:
image: mysql:8.0
container_name: ghost-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=choose_a_root_password
- MYSQL_DATABASE=ghost
- MYSQL_USER=ghost
- MYSQL_PASSWORD=choose_a_db_password
volumes:
- ./mysql:/var/lib/mysql
ghost:
image: ghost:latest
container_name: ghost
restart: unless-stopped
depends_on:
- ghost-db
environment:
- NODE_ENV=production
- url=https://blog.plainshawk.co.uk
- database__client=mysql
- database__connection__host=ghost-db
- database__connection__database=ghost
- database__connection__user=ghost
- database__connection__password=choose_a_db_password # Must match above
- mail__transport=SMTP # Optional — see Stage 4
- mail__from=noreply@plainshawk.co.uk
- mail__options__host=smtp.yourprovider.com
- mail__options__port=587
- mail__options__auth__user=your_smtp_username
- mail__options__auth__pass=your_smtp_password
volumes:
- ./content:/var/lib/ghost/content
networks:
- default
- proxy-net
networks:
default:
proxy-net:
external: trueNote on mail: The SMTP settings are optional for getting started — Ghost will work without them, but you won't get email notifications or member sign-up emails. You can add them later. If you don't want to configure mail now, remove those four mail__ lines.
Then, in Cloudflare, we can add a record to point to the blog server:
| Type | Name | Content | Proxy |
|---|---|---|---|
| A | blog | your public IP | Grey cloud (DNS only) |
We can now use docker compose to download and start the instance, which could take 20 - 30 seconds to initialise.
docker compose up -dWe can check the logs to see when it is stable, looking for a '...completed' log entry.
docker compose logs -f ghostNext, we need to add a proxy host in NPM. Log into NPM at http://<mac-mini-local-ip>:81 → Proxy Hosts → Add Proxy Host
In the Details tab:
| Field | Value |
|---|---|
| Domain | blog.plainshawk.co.uk |
| Scheme | http |
| Forward Hostname | ghost |
| Forward Port | 2368 |
| Cache Assets | Off |
| Block Common Exploits | On |
| Websockets Support | On |
SSL tab:
- Request new Let's Encrypt certificate
- Force SSL: On
- HTTP/2 Support: On
Advanced tab:
nginx
client_max_body_size 50M;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
proxy_pass http://ghost:2368;
proxy_cache_valid 200 1d;
add_header Cache-Control "public, max-age=86400";
}Save all that and NPM will set up the certificate and complete the proxy.
Complete the Ghost Setup Wizard
Navigate to https://blog.plainshawk.co.uk/ghost — this is the admin panel.
You'll be greeted by the setup wizard:
- Create your account — name, email, password
- Invite staff — skip this for now if it's just you
- Choose a theme — you can change this any time
Your blog's public-facing URL will be https://blog.plainshawk.co.uk.
Key Admin Settings to Configure
Once logged into the Ghost admin panel at /ghost:
Settings → General:
- Set your publication name and description
- Upload a logo and icon
Settings → Design:
- Choose or upload a theme
- Ghost's default theme (Casper) is clean and fast
Settings → Members (if you want a newsletter/membership site):
- Enable or disable member sign-ups
- Configure subscription tiers if needed
- This is where you'd connect Stripe for paid memberships
Settings → Integrations:
- Connect Zapier, Unsplash, or other services if needed
- Generate API keys here if you want to use Ghost as a headless CMS
Stage 8 — Keeping Ghost on the External Volume (Optional)
If you'd prefer Ghost's content (images, themes, uploads) to live on /Volumes/MediaHome rather than the Mac Mini's SSD:
Edit docker-compose.yml and change the volume:
yaml
volumes:
- /Volumes/MediaHome/ghost-content:/var/lib/ghost/contentThen recreate the container:
bash
docker compose down
docker compose up -dQuick Reference
| URL | Purpose |
|---|---|
https://blog.plainshawk.co.uk | Public-facing blog |
https://blog.plainshawk.co.uk/ghost | Admin panel |