Install Ghost in Docker, with SSL and Mail

Install Ghost in Docker, with SSL and Mail

Hi everyone,

I recently updated Ghost to the latest version, and ran into a few issues with sending email and using SSL.

In this post, we are going to setup nginx as our web server on the host, enable SSL with Let's Encrypt, and configure Ghost inside Docker with mail support for subscribers.

If you are not familiar, Ghost is a blogging platform, just like WordPress and others. I have been using Ghost since it was originally released in 2013. I really enjoy how lightweight it is compared to others and that is support markdown by default :).


If you don't have nginx already installed, you can use the following command on Ubuntu to get it installed

sudo apt update

sudo apt install nginx

The default configuration can be found in /etc/nginx/nginx.conf.

My default configuration is pretty simple, as I configure nginx to use sub-domains and proxies.


user www-data;
worker_processes auto;
pid /run/;

events {
	worker_connections 768;
	# multi_accept on;

http {

	# Basic Settings

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;

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

	# Logging Settings

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

	# Virtual Host Configs

	include /etc/nginx/sites-enabled/*;


Here are the highlights of the configuration:

  • We set the user and pid file for nginx.
  • We will review the SSL section in more detail when we setup Let's Encrypt
  • Logging sets where nginx will store access and error information
  • The virtual hosts is the most important. We tell nginx to look in /etc/nginx/sites-enabled for our sub domains

Now its time to setup our proxy route to docker. Some people run their Ghost instance's as a sub-domain like ''. There are a lot of examples online which sets up Ghost like that. On my server, I run Ghost as the main route. As you saw from our nginx config, we can put our sub-domains and proxy domains inside the sites-enabled folder.

Lets create a file in /etc/nginx/sites-enabled with the name of website. For me, that's


server {

	server_name <YOUR DOMAIN>.com www.<YOUR DOMAIN>.com;

	location ~ /.well-known {
		allow all;

	location / {
		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;
  • We start by giving the server name and saying people can go directly to the website with www. in front.
  • The .well-known part is important for Let's Encrypt. We will learn in the next section how to set this up. But Let's Encrypt uses the /.well-known directory to validate your certificates. Since we are going to be running Ghost from inside docker, we will manually create the .well-known directory in our web root.
  • The proxy information tells nginx to forward all requests to localhost on port 2368. This is the default port for Ghost.
  • We are also forwarding some of the header information as part of the redirect.


To setup Let's Encrypt for our SSL certificates, we can follow the Digital Ocean blog post for your desired OS. The blog post can be found here

Continuing from our nginx configuration. In order for Let's Encrypt to validate you own the server, they need requests to your .well-known directory to respond. Our nginx configuration says that if someone requests /.well-known to allow the request and point to the directory in the web root directory. For default installations, this will be /var/www/.well-known. All other requests will point to our docker instance running on port 2368. Since we are going to run Ghost from docker, we will create a .well-known server in /var/www on our host. Although you could setup Let's Encrypt from within the docker image, this approach allows us to use the official Ghost docker image. It also allows us to easily create new SSL certs for other sub domains and applications you run from the same server.

So, lets create the new directory in our web root.

mkdir /var/www/.well-known
chmod 755 /var/www/.well-known

Ghost - Docker

Now that we have finished setting up the server, we can now install docker and setup Ghost.

If you don't have docker installed, you can 'apt install' it.

After that, we can grab the latest ghost image from docker hub using the below command. The docker hub image can be found here.

docker pull ghost:latest

Before we launch our ghost instance, make sure that you have your Mailgun account setup. We will leverage Ghost's build-in integration with Mailgun to handle e-mails and subscribers to your blog.

You can follow Ghost's instructions to setup Mailgun here. Ghost also has other mail integration, but I found that Mailgun is better documented.

After all of that is finished, we can launch Ghost with the following command

docker run \
--name ghost-prd \
-p 2368:2368 \
-e url= \
-e mail__transport=SMTP \
-e mail__options__service=Mailgun \
-e mail__options__auth__user=<MAILGUN email address> \
-e mail__options__auth__pass=<MAILGUN password> \
-v /var/www/ghost/ghost_prd:/var/lib/ghost/content \
Parameter Description
--name The name of the instance we are going to give our docker container
-e url The base URL for our blog. Notice that https is being used
-e mail__transport The mail server type
-e mail__options__server The mail provider
-e mail__options_auth_user The user for the email provider
-e mail__options_auth_pass The user password for the email provider
-v The docker volume for our container, to store content persistently. /var/www/ghost/ghost_prd is your servers host location and /var/lib/ghost/content is the directory within the container
ghost The docker image to start

After the container has started, open your web browser and see if you can access your website. You should see the default Ghost post. After you have finished testing, you can stop the container with Ctrl+C, and then execute the following to start it in the background

docker start ghost_prd

If you would prefer to start the container in the background from the start, you can add '-d' to the original docker command. I prefer to start it in the foreground so I can easily review the log and check that everything is working properly.


In this post, we setup our server to use nginx as a webserver, enabled SSL, and deployed Ghost inside a docker container.

If you are interested in seeing your SSL score, feel free to test your website at ssllabs - (

I hope you found this post helpful. Sound off on the comments below.