Project: Build and self host your own website with Ghost using Docker
There's a lot of free, open source software out there. When stuff is free, companies make money by offering services catered around the free product (or by monetising your data) - in this case, Ghost is more or less a website builder (specialising in blogs, like this one for example, but not limited to just blogs).
It is completely free and open source, but the good people at Ghost offer hosted solutions if you don't want to go through the hassle.
This post is not about hosted solutions, but about self hosting stuff for fun - or saving your money and running things yourself. This is not to say that Ghost's services are not worthwhile.
This post will go through a basic installation of Ghost using Docker. Ghost has many features - some of them quite useful. Nowadays it supports analytics (using TinyBird), ActivityPub, newsletters, and a bunch of stuff that is covered in the their official documentation. The free plan on TinyBird won't get you very far for analytics; the newsletter feature is awesome, but setting it up implies getting a domain, a Mailgun account, setting up some DNS records; so I won't cover these more advanced features yet.
We'll address getting a domain and automating SSL certificates for that domain in a future post. These will be needed if you actually want your website to be accessible over the internet, but including instructions for the above is out of scope for this post - we will keep things simple for now.
Docker
Docker and Docker Compose (comes with Docker) will be needed for this adventure, so make sure you have them installed on your machine. You can find some information about this below.
Portainer/Dockge or any other tools are completely optional, so I won't go in too many details about them and stick with the typical compose+env set up for simplicity.
Ghost
Keep things organised - create a folder on your machine and note down the location. You will be using this path for the ${dockerpath} variable.
Using a text editor, in this folder we'll create a compose.yaml file with the following:
services:
ghost:
image: ghost:latest
restart: unless-stopped
networks:
- ghost-network
ports:
- "2368:2368"
environment:
database__client: mysql
database__connection__host: db
database__connection__user: ${database__connection__user}
database__connection__password: ${database__connection__password}
database__connection__database: ghost
url: http://${ip}
volumes:
- ${dockerpath}/ghost/content:/var/lib/ghost/content
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
restart: unless-stopped
networks:
- ghost-network
environment:
MYSQL_DATABASE: ghost
MYSQL_ROOT_PASSWORD: ${root__database__connection__password}
MYSQL_USER: ${database__connection__user}
MYSQL_PASSWORD: ${database__connection__password}
volumes:
- ${dockerpath}/ghost/db/docker-db:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
networks:
ghost-network:
driver: bridgeIf you're using something like Portainer, this would be the "stack".
Environmental variables (like ${dockerpath}) are very useful. Plain docker compose uses them through an .env file in the same folder as your compose.yaml file. You list the variables mentioned in the compose.yaml file with the values you want them to have in the context of your Ghost instance.
In this case, in the same folder where we have created the .yaml file we'll just use our text editor to create a file named .env and paste the following:
root_database__connection__password=AVerySecurePassword
database__connection__user=YourDatabaseUser
database__connection__password=AnotherVerySecurePassword
dockerpath=/yourpath/to/yourfolder
IP=localhost:2368Tools like Portainer has the ability to set environmental variables within the "stack" creation.
To explain the variables above :
- root_database_connection_password - this is the root password for your Database container. It will be used for the root account, so make sure it's different from the database__connection__password variable. This is used for health checks, and a break glass account into the database if you ever need it.
- database__connection__user - is the username used by ghost to access the database.
- database__connection__password - is the password used for the database user.
- dockerpath - this is the actual location on your machine where you'll store all this stuff.
- IP - this is the IP of the machine that will be running Ghost. If the machine's IP changes, you'll need to change this to reflect the new IP.
Deploying via docker compose is straightforward - all you need to do is run:
docker compose up -dwithin the folder where the compose.yaml and env files are.
Once deployed, you should be able to access your Ghost instance from the machine it is running on, at the http://localhost:2368 URL (or at http://IP:2368 if it's running on a different machine on the LAN). Ghost has an admin console on http://localhost:2368/ghost to manage metadata, content, themes, design and any other settings.
You now have a functional (albeit minimal) ghost installation that runs locally. Congratulations! This should be reachable by all devices on the LAN, but not yet available on the World Wide Web.
In a future post, we'll explore setting up a domain, automating SSL certificates for it, configuring Ghost for SMTP transactional emails, setting up MailGun and Ghost for newsletters, and making Ghost (or any other service) available through the internet.
Member discussion