Hosting Ghost Blog with Docker on NixOS
As previously mentioned, I have successfully deployed NixOS on my Oracle ARM machine. You can find the original post here:
In the past, my blog was hosted on Tencent Cloud using Typecho. Unfortunately, due to unforeseen circumstances, I lost ownership of that machine along with all my previous posts. Consequently, I took a hiatus from blogging, remaining in a state of silence for a few years. However, I now realize the importance of reviving my blog before lethargy engulfs me.
After conducting extensive research and considering various platforms such as Ghost, WordPress, Typecho ,Hugo and some other platforms, I finally settled on Ghost. Its remarkable speed, plethora of customized themes, aesthetically pleasing web user interface, and integrated membership system influenced my decision.
Check out all the cool stuff Ghost has to offer on their website below:
Due to the absence of Ghost in the NixOS packages, and the cumbersome nature of adapting it into a NixOS service, Docker has emerged as an excellent solution for hosting Ghost. Here, I have provided a comprehensive breakdown of the steps I followed to set up a blog using Ghost with Docker on NixOS. This can be modified to use on other platforms.
Step 0: Enable Docker on NixOS
Enabling Docker(Podman) on NixOS is a straightforward process, requiring modification of just one configuration file. I personally prefer using the vim editor, but feel free to use your preferred tool such as nano
, emacs
, or VS Code
.
The initial step involves logging into the machine, particularly if it is being used as a server.
ssh ${username}@${server IP}
Then, we can start to modify the configuration file:
sudo vim /etc/nixos/configuration.ni
There are two ways of adding Docker to the NixOS system: for all users:
environment.systemPackages = with pkgs; [
docker
];
And for one user only:
users.users.${username}.packages = with pkgs; [
docker
];
You can choose either way based on your needs. The next step is to enable the Docker service.
virtualisation.docker.enable = true;
virtualisation.oci-containers.backend = "docker";
Note that we're using oci-containers
to control Dockers. If you have chosen to install Podman, remember to modify it accordingly. Some may question why we're not using docker-compose
; this is a simple answer – we embrace the capabilities of NixOS
, and that suffices.
Last, remember to create a directory for docker to use. Here's my example:
mkdir ~/.docker
Step 1: Set up Docker Network
Using the Docker CLI command docker network
will indeed create the network, but it may not be the optimal approach. Since we're operating within the context of NixOS
, we can add it as a service. Add the following code snippet to your configuration.nix
file, ensuring to customize the name according to your requirements. In my case, I'm utilizing npm
as an example since I'm employing nginx-proxy-manager
as my Nginx reverse proxy service.
systemd.services.init-docker-ghost-network-and-files = {
description = "Create the network npm for nginx proxy manager using reverse proxy.";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot";
script =
let dockercli = "${config.virtualisation.docker.package}/bin/docker";
in ''
# Put a true at the end to prevent getting non-zero return code, which will
# crash the whole service.
check=$(${dockercli} network ls | grep "npm" || true)
if [ -z "$check" ]; then
${dockercli} network create npm
else
echo "npm already exists in docker"
fi
'';
};
Step 2: Set up Mysql for Ghost
We will now proceed with crafting Docker configurations. The initial step involves creating an external directory for MySQL to store its data, ensuring that we can modify MySQL without accessing the Docker environment directly. At present, this MySQL database is exclusively intended for Ghost; however, you have the freedom to tailor it according to your specific requirements.
mkdir ~/.docker/ghost-blog/mysql -p
Please add the following snippet to your configuration file as well:
virtualisation.oci-containers.containers."ghost-db" = {
image = "mysql:latest";
volumes = [ "/home/hua/.docker/ghost-blog/msql:/var/lib/mysql" ];
environment = {
MYSQL_ROOT_PASSWORD = "your_mysql_root_password";
MYSQL_USER = "ghost";
MYSQL_PASSWORD = "ghostdbpass";
MYSQL_DATABASE = "ghostdb";
};
extraOptions = [ "--network=npm" ];
};
Please note that Ghost no longer supports SQLite
and MariaDB
as its database options.
Step 3: Set up Ghost Docker
Finally, It's time for Ghost.
Basic Set up Configuarion
Following the previous instructions, we will proceed to create the content
folder:
mkdir ~/.docker/ghost-blog/content
Now, let's move on to configuring Ghost:
virtualisation.oci-containers.containers."ghost-blog" = {
image = "ghost:latest";
volumes =
[ "/home/hua/.docker/ghost-blog/content:/var/lib/ghost/content" ];
dependsOn = [ "ghost-db" ];
ports = [ 3001:3001 ];
environment = {
NODE_ENV = "develop";
url = "http://${server IP}:3001";
database__client = "mysql";
database__connection__host = "ghost-db";
database__connection__user = "ghost";
database__connection__password = "ghostdbpass";
database__connection__database = "ghostdb";
};
extraOptions = [ "--network=npm" ];
};
Within this section, we configure the port mapping, environment variables, and volume mapping. Please note that you should customize the MySQL configurations in accordance with your specific setup in the final step.
Mail Server Set Up
Taking Gmail as an example, please note that you can modify this configuration according to your specific needs.
virtualisation.oci-containers.containers."ghost-blog".environment = {
mail__transport = "SMTP";
mail__option_service = "Google";
mail__options__auth__user = "username@gmail.com";
mail__options__auth__pass = "your google app password";
mail__options__host = "smtp.gmail.com";
mail__options__port = "587";
mail__options__secure = "false";
mail__from = "username@gmail.com";
tls__rejectUnauthorized = "true";
}
Please remember that the Google app password mentioned here is different from your actual Google account password. You can generate a Google app password by following the steps outlined in the Sign in with app passwords guide.
By configuring these settings, visitors will be able to sign up and leave comments on our website.
More Custom Options
Please refer to the instructions provided on the Ghost website at the following link:
Step 4: Set up Nginx Reverse Proxy
There are numerous articles available on the internet that explain how to set up Nginx as a system service or utilize nginx-proxy-manager
as a Docker service. For the purpose of this example, I will demonstrate the Docker service approach. Remember to create the necessary folders as well.
virtualisation.oci-containers.containers."nginx-proxy-manager" = {
image = "jc21/nginx-proxy-manager:latest";
dependsOn = [ "ghost-blog" "chatgpt-next-web" ];
volumes = [
"/home/hua/.docker/nginx-proxy-manager/data:/data",
"/home/hua/.docker/nginx-proxy-manager/letsencrypt:/etc/letsencrypt"
];
ports = [ "80:80", "443:443", "81:81" ];
extraOptions = [ "--network=npm" ];
};
Step 5: Rebuild System
sudo nixos-rebuild switch`
Step 6: Start to Use
After rebuilding the system, you can proceed to open the web pages for both Ghost and nginx-proxy-manager.
For information and usage details about Ghost, please visit:
To learn more about nginx-proxy-manager, please visit:
Please note that once you have set up the nginx reverse proxy for Ghost, it's necessary to modify the Docker configuration for Ghost as follows:
virtualisation.oci-containers.containers."ghost-blog".environment = {
NODE_ENV = "production";
url = "https://your-website-address";
}
Please replace your-website-address
with the actual address of your website. After making this modification, rebuild the system again.
In conclusion, if you have any further questions, please feel free to leave a comment without hesitation.