Ghost + Docker + Nginx + SSL
Published: 2023-04-29 22:26:30

---
title: Ghost + Docker + Nginx + SSL
published: true
date: 2020-10-07 13:09:00 UTC
tags: 
canonical_url: https://mikestaszel.com/2020/10/07/ghost-docker-nginx-ssl/
---

Setting up a blog with Ghost is extremely easy. You don't need to worry about NodeJS, SQL databases, or whether your machine will be able to run it. Last time I used Ghost, I remember messing around with NodeJS and NPM. Nowadays, it's much easier. You can run this on a machine with 1 CPU core and 512MB of RAM (you can get one of these on the GCP free tier).

At the end of this guide you'll have a Ghost blog with a free, auto-renewing SSL certificate.

### Installing Ghost and Nginx

First, install Docker. Your Linux distribution's version of Docker is just fine.

```
sudo apt install -y docker.io
sudo systemctl enable docker
sudo gpasswd -a YOURUSERNAME docker
# now, log out of the terminal and log back in
```

Next, run:

```
mkdir -p $HOME/content
docker run \
    --name ghost \
    -p 2368:2368 \
    -e url=https://example.com \
    -v $HOME/content:/var/lib/ghost/content \
    --restart=always \
    -d \
    ghost:alpine
```

You should save this in a script called `create.sh` if you ever need to recreate the container.

Next, install nginx `(sudo apt install -y nginx)` and modify the configuration:

```
# /etc/nginx/nginx.conf:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
}

http {
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	server_tokens off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_prefer_server_ciphers on;

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	gzip on;

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

# /etc/nginx/sites-enabled/default:

server {
    server_name example.com;
    listen [::]:80;
    listen 80;

    location / {
        proxy_pass http://127.0.0.1:2368;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

Now, start Nginx: `sudo systemctl restart nginx`

Now we'll set up an SSL certificate using Let's Encrypt and CertBot. You'll need to get your Linux distribution's exact instructions from this page: [https://certbot.eff.org/lets-encrypt/debianbuster-nginx](https://certbot.eff.org/lets-encrypt/debianbuster-nginx)

Select "Nginx" and your distribution. On Debian, it was as simple as:

```
sudo apt-get install certbot python-certbot-nginx
sudo certbot --nginx
sudo reboot
```

When the system starts back up, your Ghost blog should be up and running with SSL!

### Updating Ghost

When it's time to update, all I have to do is:

```
docker stop 123
docker rm 123
docker pull ghost:alpine
./create.sh
```

Since my content is mounted to the container, I can delete and recreate the container without worrying.

### Backups

Backing up the content is straightforward too. Since I'm using Ghost with SQLite, all I need to do is back up my `content` directory. I use `rclone` to back this up to Backblaze B2 (10GB free!) on a daily basis.

```
FILENAME=backup_`date '+%Y%m%d'`.tar.gz
tar -zcf backups/$FILENAME content
rclone copy backups/$FILENAME b2:/my-bucket/blogbackup/
```

That's all there is to it. I'm very happy with my lightweight Ghost blog set up!