Hosting Ghost blog on Amazon Lightsail (Ubuntu, Docker)

August 02, 2017 - IT

I assume that you have an account on AWS. Log in, go to Amazon Lightsail and click Create Instance. During this process, you can choose where your instance should be located (region and availability zone) and which system should be installed. I chose Frankfurt (eu-central-1a) and Ubuntu Linux.

AWS Lightsail - new instance
AWS Lightsail - new instance

We won't need launch scripts but we will have to create SSH key pair. We will need it later for connecting to the instance. Click Change SSH key pair and click Create new. Type a name, click Generate key pair, download keys and put them in ~/.ssh directory. We have to change key permissions before use.

chmod 400 ~/.ssh/eshlox-net.pem

AWS Lightsail - SSH keys
AWS Lightsail - SSH keys

Next, choose a plan. I choose the cheapest plan, it's enough for me.

AWS Lightsail - plans
AWS Lightsail - plans

The last step, name your instance and click Create button.

AWS Lightsail - name your instance
AWS Lightsail - name your instance

We have to wait for the instance to be ready. When the instance has a status Running, we can log in.

We should attach a static IP. Go to AWS Lightsail, select Create static IP button, choose the same region and zone which were chosen for your instance, attach it to your instance, select IP address name, and click Create. From now on, you can use the newly created IP address and your blog will be accessible under it.

We can log in to the instance in two ways. We can use web SSH client or use SSH from our system. I choose the second one. Select Your instance and get the public IP address. In my case, the SSH command looks like this:

ssh -i ~/.ssh/eshlox-net.pem [email protected]

First thing after login, let's update our system.

sudo apt update && sudo apt upgrade

Now, let's install Docker. You can check Docker documentation if you want to read more about Docker installation. I will post here only required commands without description.

curl -fsSL | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] $(lsb_release -cs) stable"
sudo apt update
sudo apt install docker-ce

We want to execute Docker commands without sudo, let's add ubuntu user to the docker group.

sudo groupadd docker
sudo usermod -aG docker $USER

Configure Docker to start on boot.

sudo systemctl enable docker

Log out, log back and run docker info to check if Docker works.

Time to install Ghost blog. In this post I will use Ghost in version 0.11.11. If you read this post in the future, change version to the newest one.

Create the directory where you will keep blog data.

mkdir ~/blog_content

The command below will do this:

  • download Ghost in version 0.11.11
  • run Docker container
  • mount our blog_content directory to /var/lib/ghost inside Docker container and store blog data there
  • map port 2368 to 80 so we will be able to access our blog on port 80

Of course, you can change the name of the container.

docker run --name eshlox-net -v /home/ubuntu/blog_content:/var/lib/ghost -p 80:2368 ghost:0.11.11-alpine

The output of the command should look like this:

[[email protected] ~]$ docker run --name eshlox-net -v /home/ubuntu/blog_content:/var/lib/ghost -p 80:2368 ghost:alpine
Unable to find image 'ghost:alpine' locally
alpine: Pulling from library/ghost
90f4dba627d6: Pull complete
302ce48cc185: Pull complete
e1c17ba3935d: Pull complete
1a86fd5d5e80: Pull complete
e6139c361248: Pull complete
8a700ec450ff: Pull complete
e8db228789f6: Pull complete
5b4133c4fb9c: Pull complete
c97fdc920c32: Pull complete
Digest: sha256:acda68ca051787139a162b8748dfd5efbccb45f98899ad0d76f30220c65dd47b
Status: Downloaded newer image for ghost:alpine
npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm info prestart [email protected]
npm info start [email protected]

> [email protected] start /usr/src/ghost
> node index

WARNING: Ghost is attempting to use a direct method to send email.
It is recommended that you explicitly configure an email service.
Help and documentation can be found at

Migrations: Creating tables...
Migrations: Creating table: posts
Migrations: Creating table: users
Migrations: Creating table: roles
Migrations: Creating table: roles_users
Migrations: Creating table: permissions
Migrations: Creating table: permissions_users
Migrations: Creating table: permissions_roles
Migrations: Creating table: permissions_apps
Migrations: Creating table: settings
Migrations: Creating table: tags
Migrations: Creating table: posts_tags
Migrations: Creating table: apps
Migrations: Creating table: app_settings
Migrations: Creating table: app_fields
Migrations: Creating table: clients
Migrations: Creating table: client_trusted_domains
Migrations: Creating table: accesstokens
Migrations: Creating table: refreshtokens
Migrations: Creating table: subscribers
Migrations: Running fixture populations
Migrations: Creating owner
Ghost is running in development...
Listening on
Url configured as: http://localhost:2368
Ctrl+C to shut down

Now, you can access your blog using public IP address which is attached to your instance.

Ghost blog - hello
Ghost blog - hello

Now, we have to set up Docker container to run it automatically when the system starts.

Click Ctrl+C to kill container with Ghost blog.

Create /etc/systemd/system/[email protected] file. Of course, change eshlox-net to your container name. File content:

Description=Docker Container %I

ExecStart=/usr/bin/docker start -a %i
ExecStop=/usr/bin/docker stop -t 2 %i


Now, let's edit this service and configure our container.

systemctl edit [email protected]

This command will open a text editor. Put this content there:

ExecStart=/usr/bin/docker run -v /home/ubuntu/blog_content:/var/lib/ghost -p 80:2368 -e NODE_ENV=production --name %i ghost:0.11.11-alpine
ExecStopPost=/usr/bin/docker rm -f %i

Notice that the line which starts with ExecStart contains similar parameters as our previous command.

Reload systemctl to apply our changes.

systemctl daemon-reload

Start our container.

sudo systemctl start [email protected]

Enable newly created service to start our container on system boot.

sudo systemctl enable [email protected]

Go to your public IP and check if Ghost blog works. You can also restart the instance and check if it automatically starts on boot.

How to update Ghost?

It's very simple.

Edit our service configuration.

systemctl edit [email protected]

Change the Ghost version, for example, change

ExecStart=/usr/bin/docker run -v /home/ubuntu/blog_content:/var/lib/ghost -p 80:2368 -e NODE_ENV=production --name %i ghost:0.11.10-alpine


ExecStart=/usr/bin/docker run -v /home/ubuntu/blog_content:/var/lib/ghost -p 80:2368 -e NODE_ENV=production --name %i ghost:0.11.11-alpine

Restart service.

sudo systemctl restart [email protected]

It will automatically download new Ghost image and use it when the container starts.

How to check container logs?

It useful when you want to debug issues with Ghost or Docker container.

journalctl -u [email protected] -b

As always, change your eshlox-net.service to your service name.

AWS Lightsail allows us to create a backup which I recommend to do at least before every system update and Ghost blog update. If something breaks, you can use a snapshot to restore the system.