Set up Ghost with Podman on a VPS

Set up Ghost with Podman on a VPS
Photo by Tandem X Visuals / Unsplash

I recently moved this website / blog from Jekyll to Ghost, just to try it out after hearing quite a lot of nice buzz about it lately.

I had this small personal VPS at hand, so I decided to give it some space there, and use Podman (and its quadlets feature) to set it up. This leverages Systemd to run your docker images. Here is a quick walkthrough, should anyone be interested:

Obviously, you need a VPS with SSH access. It must have its firewall open to the web on port 443. My VPS is running Ubuntu, so my install commands will reflect this. I also chose to use root mode to run the quadlets, but there are other options available.

Setup

First, install Podman:

sudo apt install podman podman-docker podman-toolbox

Pull the required images from docker:

sudo podman pull docker.io/mysql:8
sudo podman pull docker.io/ghost:5-alpine
sudo podman pull docker.io/caddy:alpine

The Systemd services

Create a MySQL container file at /etc/containers/systemd/mysql.container with this content:

[Unit]
Description=Blog MySQL Container
After=network-online.target

[Container]
ContainerName=blog-mysql
AddCapability=SYS_NICE
Image=docker.io/mysql:8
Volume=/srv/containers/blog/mysql/var/lib/mysql:/var/lib/mysql:Z
Pod=blog.pod
Secret=blog_db_name,type=env,target=MYSQL_DATABASE
Secret=blog_db_user,type=env,target=MYSQL_USER
Secret=blog_db_password,type=env,target=MYSQL_PASSWORD
Secret=blog_db_rootpassword,type=env,target=MYSQL_ROOT_PASSWORD

[Service]
Restart=always

[Install]
WantedBy=default.target
RequiredBy=blog-ghost.service

Create a Ghost container file at /etc/containers/systemd/ghost.container

Do not forget to change yourdomain.com with your domain.

[Unit]
Description=Ghost Container
After=blog-db.service

[Container]
ContainerName=blog-ghost
Environment=database__client=mysql database__connection__host=blog-mysql url=http://yourdomain.com
Image=docker.io/ghost:5-alpine
Pod=blog.pod
Secret=blog_db_name,type=env,target=database__connection__database
Secret=blog_db_user,type=env,target=database__connection__user
Secret=blog_db_password,type=env,target=database__connection__password
Volume=/srv/containers/blog/var/lib/ghost/content:/var/lib/ghost/content:Z

[Service]
Restart=always

[Install]
WantedBy=default.target

You need to set up the secrets used in these two files to access MySQL. Let’s do it:

printf $(tr -dc A-Za-z0-9 </dev/urandom | head -c 13) | sudo podman secret create blog_db_rootpassword -
printf $(tr -dc A-Za-z0-9 </dev/urandom | head -c 13) | sudo podman secret create blog_db_password -
printf "gdb" | sudo podman secret create blog_db_name -
printf "guser" | sudo podman secret create blog_db_user -

You also need to have the files stored on your filesystem, so let’s create the directories that are mounted in the images:

sudo mkdir -p /srv/containers/blog/var/lib/ghost/content
sudo mkdir -p /srv/containers/blog/mysql/var/lib/mysql

Now, add a blog.network file at /etc/containers/systemd/blog.network

[Unit]
Description=Custom blog Podman network

[Network]
NetworkName=blog
Driver=bridge

And a blog.pod file at /etc/containers/systemd/blog.pod

[Unit]
Description=Blog Pod

[Pod]
PodName=blog
Network=blog.network
PublishPort=3001:2368

Caddy set up

Let’s use Caddy to serve the app to the internet. I chose to leave Caddy out of the blog pod context because I use it to proxy other toy servers on that VPS.

Create the service file at /etc/containers/systemd/caddy.container

[Container]
ContainerName=caddy
Image=docker.io/caddy:alpine
PublishPort=80:80
PublishPort=443:443
Volume=/srv/containers/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
Volume=/srv/containers/caddy/data:/data
Volume=/srv/containers/caddy/config:/config
Network=host

[Service]
Restart=always

[Install]
WantedBy=default.target

And create the Caddy directory:

sudo mkdir -p /srv/containers/caddy

Add a Caddyfile at /srv/containers/caddy/Caddyfile with the following content:

{
    # email to use on Let's Encrypt
    email [email protected]
}

www.yourdomain.com {
        redir https://yourdomain.com{uri}
}

yourdomain.com {
  reverse_proxy 127.0.0.1:3001
}

Do not forget to update the file with your email and domain.

Go live

Now let’s open the gates...

sudo systemctl daemon-reload
sudo systemctl enable caddy
sudo systemctl start caddy
sudo systemctl enable blog-pod
sudo systemctl start blog-pod

And visit yourdomain.com/ghost to start blogging!

Sources

Deploying a multi-container application using Podman and Quadlet
In How to “build once, run anywhere” at the edge with containers, I hint that version 4.4 of Podman includes a new tool called Quadlet that enables simple wa…
Quadlet : Exécution de conteneurs podman sous systemd - Wiki
Introduction Utilisant exclusivement RHEL dans le cadre de mon travail, et quasiment que RHEL et ses clones en perso,…
podman-systemd.unit — Podman documentation
Mastodon