# discourse_docker keeps state in /shared and manages containers with one bash script

> discourse_docker is the MIT-licensed Docker packaging for Discourse, built around a launcher script, pup-managed templates and container definitions you own in /containers. It offers a one-container install and a multi-container layout that trades simplicity for upgrade and scaling options.

**discourse/discourse_docker** — A Docker image for Discourse

- Repository: https://github.com/discourse/discourse_docker
- Website: http://www.discourse.org
- Stars: 1,863 · Forks: 814
- Language: Shell
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/discourse-discourse-docker

## One bash script is the entire control plane

The base directory of this repository contains a single bash script called launcher, and it is what you use for every lifecycle operation on a container. Its usage line is launcher COMMAND CONFIG, with two optional flags, --skip-prereqs and --docker-args STRING.

The commands are the ones you would guess and a few you would not. start, stop, restart and destroy cover the normal lifecycle. enter opens a shell inside a running container and logs shows its Docker logs. bootstrap creates a container from a config based on a template. run executes a command with that config in the context of the last bootstrapped image, which is how you run a one-off management task. rebuild destroys the old container, bootstraps again and starts the new one. cleanup removes every container that has been stopped for more than 24 hours. start-cmd prints the docker command that would be used, which is the debugging entry point when a container will not come up.

One environment variable changes the operational model. If SUPERVISED is set to true the container does not detach, which lets an external process monitoring tool own the restart behaviour instead of Docker's own restart policy.

This is a script-driven interface, not a Compose file. There is no docker-compose.yml in this repository; the equivalent of an orchestrator is a set of container definitions plus the launcher.

## Templates compose, and hooks are how you extend them

A container definition is YAML with a handful of special sections at the top, and the first is templates, which is a list of other templates composed into this one:

```yaml
templates:
  - 'templates/cron.template.yml'
  - 'templates/postgres.template.yml'
```

Those child templates live in /templates and are described as pup-managed, referring to the pup tool the Discourse team uses for templating. 

The other sections are the familiar container concerns with Discourse-specific detail. expose publishes ports, and the syntax is the Docker one, so 2222:22 binds host port 2222 on all interfaces while 127.0.0.1:20080:80 restricts to loopback, and a bare port number exposes without publishing. volumes maps a host directory into the container, which is how /var/discourse/shared becomes /shared. links adds a --link for a named container, with a name and an alias. env and labels set environment variables and container labels, and both examples show templating in the value with {{config}}, so a definition can derive a database name from the config name.

The extension point is hooks, which run code before or after defined stages:

```yaml
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager
```

There is no list of valid hook names in the documentation. The way to find them is to search the templates in the repository for hook: and list what comes back, which is an honest and slightly awkward answer.

## Why /shared decides whether a rebuild loses your forum

The directory layout is not arbitrary. /cids holds container ids for running containers, described as Docker's equivalent of pids, each with a unique git-like hash. /containers holds your definitions and ships empty, because you are in charge of that directory. /samples holds definitions you copy to bootstrap an environment. /templates holds the pup templates. /shared is the placeholder for volumes shared between containers.

/shared is the part to understand before your first deploy. The stated purpose is storing persistent information outside the container, and the example given is logfiles and the upload directory. The reasoning is twofold: you can rebuild containers without losing important information, and keeping uploads outside lets multiple web instances share them.

That last point is what makes multi-server possible, and it is also the failure mode. If your uploads live inside a container rather than in a host directory you mount, a destroy followed by a rebuild takes your attachments with it, and the README's rebuild command does exactly that destroy step.

The image directory holds the Dockerfiles, and the README says the Docker repository always contains the latest built base version, so you should not need to build the base image yourself. The repository also carries bin/, scripts/, install-discourse, discourse-setup and discourse-doctor, which are the entry points around the launcher.

## Standalone is easy, multiple containers is what you pay for

The samples directory is where the two deployment shapes are defined. samples/standalone.yml bundles all the software required to run Discourse into a single container, and the README calls the advantage that it is easy. The README also states the easiest way to self-host is the standalone template, installable in 30 minutes or less, with detailed instructions in the Discourse repository's INSTALL-cloud.md.

The multi-container setup is described as far more flexible and robust, and more complicated. What you gain is concrete: you can bootstrap new web processes while the site runs and only switch the new image in once it is built, which minimises downtime on upgrade. You can scale to multiple servers. You can add servers for redundancy. And you can run the database or other required services on better hardware than the web tier.

The samples that define that shape are samples/data.yml and samples/web_only.yml, with samples/redis.yml and samples/mail-receiver.yml for the supporting services. To ease the split across hosts, launcher injects an environment variable called DISCOURSE_HOST_IP that is available inside the image, so containers can find the database host.

The README's warning about multiple container setups is truncated in the visible text, which is unfortunate because it is exactly the kind of configuration mistake that costs an afternoon.

## Upgrades happen in the admin panel or through rebuild

Two upgrade routes are documented. The first is the web front end at /admin/upgrade on your running site, which upgrades an already running image. The second is manual, rebuilding the image yourself:

```bash
./launcher rebuild my_image
```

That command destroys the old container, bootstraps from your definition and starts a new one, which is why the /shared discussion above is not academic. A rebuild is safe only if your persistent state is on the host.

The upgrade story is also where the single-container and multi-container split pays for itself, because the documented way to get near-zero downtime is to bootstrap new web processes while the site is still running and switch the image in afterwards. With one container you take the rebuild window.

A caveat on image tags is worth stating plainly: the repository has no GitHub releases, and the base image is pulled from Docker Hub at discourse/base with a latest build. Nothing in this documentation tells you how to pin a known-good base image, so record the image digest you run before you upgrade, not after.

## How this compares with the official hosting and with Compose

Discourse also sells hosting, and the README puts it in a callout right at the start of getting started, stating that the services directly support development of the project. That is the honest comparison for anyone evaluating this repository: self-hosting gives you the data and the hardware, and costs you the operations.

Against other self-hosted forum stacks, the difference is that discourse_docker is not a generic container bundle. It ships defaults the Discourse team considers optimal, per the README, and it assumes a multi-machine topology with data, web, redis and mail containers that link to each other by name. 

Against running Discourse from source directly, the gain is the container boundary and the ability to rebuild the application while keeping the database container untouched, and the cost is an extra layer between you and the Ruby application when something breaks.

Licensing is MIT for this repository.

## Conclusion

discourse_docker suits a self-hoster who wants Discourse running on their own hardware and is prepared to own the container definitions, since the templates, the launcher and the /shared volume are all things you edit and keep. It is the wrong choice if you need a support contract, because the README advertises official hosting instead, and the multiple-container layout is only worth its complexity past one server. Verify first that the samples you copy into /containers match your host's disk layout and that everything under /shared is actually persistent in your storage, because an uploads directory left inside a container is where rebuilds lose data.

## FAQ

### How do I upgrade a Discourse Docker installation?

Either use the front end at /admin/upgrade on the running site, or rebuild the image manually with ./launcher rebuild my_image, which destroys the old container, bootstraps and starts a new one.

### What does the launcher script do in discourse_docker?

It manages containers with the form launcher COMMAND CONFIG, including start, stop, restart, destroy, enter, logs, bootstrap, run, rebuild, cleanup and start-cmd. Setting the environment variable SUPERVISED to true leaves the container attached so an external monitoring tool manages restarts.

### Should I use the standalone template or multiple containers?

The standalone template in samples bundles everything into one container and is described as easy to install in 30 minutes or less. The multi-container setup using samples/data.yml and samples/web_only.yml is more complex but allows rolling upgrades with less downtime, scaling to multiple servers, redundancy, and running the database on stronger hardware.

## Sources

- [discourse/discourse_docker on GitHub](https://github.com/discourse/discourse_docker)
- [Issues](https://github.com/discourse/discourse_docker/issues)
- [License: MIT](https://github.com/discourse/discourse_docker/blob/main/LICENSE)
- [Project website](http://www.discourse.org)
- [README](https://github.com/discourse/discourse_docker/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/discourse-discourse-docker
