Skip to content

Hosting Ghost Blog with Docker on NixOS

Posted on:August 20, 2023 at 01:05 AM

Table of contents

Open Table of contents


As previously mentioned, I have successfully deployed NixOS on my Oracle ARM machine. You can find the original post here: How to Install NixOS on Oracle ARM machine

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 GhostWordPressTypecho ,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:  Ghost.

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 nanoemacs, 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/

There are two ways of adding Docker to the NixOS system: for all users:

environment.systemPackages = with pkgs; [

And for one user only:

users.users.${username}.packages = with pkgs; [

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. = {
  description = "Create the network npm for nginx proxy manager using reverse proxy.";
  after = [ "" ];
  wantedBy = [ "" ];

  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
        echo "npm already exists in docker"

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 = "";
  mail__options__auth__pass = "your google app password";
  mail__options__host = "";
  mail__options__port = "587";
  mail__options__secure = "false";
  mail__from = "";
  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: Ghost Configuration.

Step 6: Start to Use

After rebuilding the system, you can proceed to open the web pages for both Ghost and nginx-proxy-manager.

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.