How to Install Discourse in an LXD / Docker Container

Discourse on LXD

Step 1 – Install Docker in an LXD container

We first need to launch an LXD container capable of running Docker in it.

$ lxc launch ubuntu:18.04 container -c security.nesting=true
Creating container
Starting container

Here, container is the name of the LXD container. The security.nesting feature must be set to true to allow a Docker installation inside an LXD container.

Next, let’s install Docker Community Edition on Ubuntu 18.04 (Bionic Beaver).

$ lxc exec container bash
[email protected]:~$ apt-get update
Get:1 xenial-security InRelease [102 kB]
Reading package lists... Done
[email protected]:~$ apt-get install \
> apt-transport-https \
> ca-certificates \
> curl \
> software-properties-common
Reading package lists... Done
The following additional packages will be installed:
The following packages will be upgraded:
 curl libcurl3-gnutls
2 upgraded, 0 newly installed, 0 to remove and 20 not upgraded.
[email protected]:~$ curl -fsSL | apt-key add -
[email protected]:~$ apt-key fingerprint 0EBFCD88
pub 4096R/0EBFCD88 2017-02-22
 Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
uid Docker Release (CE deb) <[email protected]>
sub 4096R/F273FCD8 2017-02-22

[email protected]:~$ add-apt-repository \
> "deb [arch=amd64] \
> $(lsb_release -cs) \
> stable"
[email protected]:~$ apt-get update
[email protected]:~$ apt-get install docker-ce
The following NEW packages will be installed:
 aufs-tools cgroupfs-mount docker-ce libltdl7
0 upgraded, 4 newly installed, 0 to remove and 20 not upgraded.
Need to get 21.2 MB of archives.
After this operation, 100 MB of additional disk space will be used.
[email protected]:~$

Third, let’s test that Docker is working, by running the hello-world image.

[email protected]:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
5b0f327be733: Pull complete 
Digest: sha256:07d5f7800dfe37b8c2196c7b1c524c33808ce2e0f74e7aa00e603295ca9a0972
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
 executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
 to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:

For more examples and ideas, visit:

[email protected]:~$

Finally, let’s go full inception by running Ubuntu in a Docker container inside an Ubuntu LXD container on Ubuntu 18.04.

[email protected]:~$ docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
ae79f2514705: Pull complete 
5ad56d5fc149: Pull complete 
170e558760e8: Pull complete 
395460e233f5: Pull complete 
6f01dc62e444: Pull complete 
Digest: sha256:506e2d5852de1d7c90d538c5332bd3cc33b9cbd26f6ca653875899c505c82687
Status: Downloaded newer image for ubuntu:latest
[email protected]:/# exit

Before we finish, let’s check what docker info says.

[email protected]:~$ docker info
Containers: 2
 Running: 0
 Paused: 0
 Stopped: 2
Images: 2
Server Version: 17.09.0-ce
Storage Driver: vfs
Logging Driver: json-file
Cgroup Driver: cgroupfs
 Volume: local
 Network: bridge host macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 06b9cb35161009dcb7123345749fef02f7cea8e0
runc version: 3f2f8b84a77f73d38244dd690525642a72156c64
init version: 949e6fa
Security Options:
 Profile: default
Kernel Version: 4.10.0-37-generic
Operating System: Ubuntu 18.04.1 LTS
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 4.053 GiB
Name: docker
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Experimental: false
Insecure Registries:
Live Restore Enabled: false

WARNING: No swap limit support
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
[email protected]:~$

Step 2 – Install Discourse

Install Discourse

Create a /var/discourse folder, clone the Official Discourse Docker Image into it:

sudo -s
mkdir /var/discourse
git clone /var/discourse
cd /var/discourse

You will need to be root through the rest of the setup and bootstrap process.


⚠️ Email is CRITICAL for account creation and notifications in Discourse. If you do not properly configure email before bootstrapping YOU WILL HAVE A BROKEN SITE!

Domain Name

🔔 Discourse will not work from an IP address, you must own a domain name such as to proceed.

  • Already own a domain name? Great. Select a subdomain such as or or for your Discourse instance.

  • No domain name? We can recommend NameCheap, or there are many other great domain name registrars to choose from.

  • Your DNS controls should be accessible from the place where you purchased your domain name. Create a DNS A record for the subdomain in your DNS control panel, pointing to the IP address of your cloud instance where you are installing Discourse.

Edit Discourse Configuration

Launch the setup tool at


Answer the following questions when prompted:

Hostname for your Discourse? []: 
Email address for admin account(s)? [[email protected],[email protected]]: 
SMTP server address? []: 
SMTP port? [587]: 
SMTP user name? [[email protected]]: 
SMTP password? [pa$word]: 
Let's Encrypt account email? (ENTER to skip) [[email protected]]: 

This will generate an app.yml configuration file on your behalf, and then kicks off bootstrap. Bootstrapping takes between 2-8 minutes to set up your Discourse. If you need to change these settings after bootstrapping, you can run ./discourse-setupagain (it will re-use your previous values from the file) or edit /containers/app.yml manually with nano and then ./launcher rebuild app, otherwise your changes will not take effect.

Important: Launcher will fail on VFS devices

While docker will run on VFS, the Discourse launcher script fails if VFS is detected.

The solution is simple, edit the launcher file and search for aufs where you’ll find valid disk options, add vfs as an option and re-run the launcher script.

Start Discourse

Once bootstrapping is complete, your Discourse should be accessible in your web browser via the domain name you entered earlier.

Discourse LXD

Register New Account and Become Admin

Register a new admin account using one of the email addresses you entered before bootstrapping.

Discourse LXD

Discourse LXD

(If you are unable to register your admin account, check the logs at /var/discourse/shared/standalone/log/rails/production.log and see our Email Troubleshooting checklist.)

After registering your admin account, the setup wizard will launch and guide you through basic configuration of your Discourse.

Discourse LXD

After completing the setup wizard, you should see Staff topics and READ ME FIRST: Admin Quick Start Guide. This guide contains advice for further configuring and customizing your Discourse install.

Discourse LXD

Post-Install Maintenance

  • We strongly suggest you turn on automatic security updates for your OS. In Ubuntu use the dpkg-reconfigure -plow unattended-upgrades command. In CentOS/RHEL, use the yum-cron package.
  • If you are using a password and not a SSH key, be sure to enforce a strong root password. In Ubuntu use the apt-get install libpam-cracklib package. We also recommend fail2ban which blocks any IP addresses for 10 minutes that attempt more than 3 password retries.
    • Ubuntuapt-get install fail2ban
    • CentOS/RHELsudo yum install fail2ban (requires EPEL)
  • If you need or want a default firewall, turn on ufw for Ubuntu or use firewalld for CentOS/RHEL 7 or later.

You will get email reminders as new versions of Discourse are released. Please stay current to get the latest features and security fixes. To upgrade Discourse to the latest version, visit /admin/upgrade in your browser and click the Upgrade button.

The launcher command in the /var/discourse folder can be used for various kinds of maintenance:

Usage: launcher COMMAND CONFIG [--skip-prereqs] [--docker-args STRING]
    start:      Start/initialize a container
    stop:       Stop a running container
    restart:    Restart a container
    destroy:    Stop and remove a container
    enter:      Use nsenter to get a shell into a container
    logs:       View the Docker logs for a container
    bootstrap:  Bootstrap a container for the config based on a template
    rebuild:    Rebuild a container (destroy old, bootstrap, start new)
    cleanup:    Remove all containers that have stopped for > 24 hours

    --skip-prereqs             Don't check launcher prerequisites
    --docker-args              Extra arguments to pass when running docker

Problems running docker in an LXD container?

If you encounter problems with Docker failing to run in LXD then you may need to tweak some settings in your host’s /etc/sysctl.conf Edit /etc/sysctl.conf and try these settings:
net.ipv4.tcp_mem=182757 243679 365514
Apply them using sysctl -p or reboot your host.


Please enter your comment!
Please enter your name here