This will be a single-region single-instance deployment — Discourse requires a data volume for uploads, and while we can use S3 or a virtual filesystem that’s not covered in this guide. Attaching a data volume on Fly anchors the instance to the region and physical host that the disk is on.
Let’s create the database first with
fly pg create — I’ll calling this database
discourse-db and choose my organization and region. Once the database has been created, I see the following message:
Creating postgres cluster discourse-db in organization personal Postgres cluster discourse-db created Username: postgres Password: babec71dc5d2fc42a04ae47d591b87b56239b232c6bbb2ad Hostname: discourse-db.internal Proxy Port: 5432 PG Port: 5433 Save your credentials in a secure place, you won't be able to see them again! ...
I can now connect to the main Postgres database by forming the credentials into a URL like
postgres://postgres:email@example.com:5432, but we don’t actually want to use the main Postgres DB — we’ll create a Discourse specific DB soon using
fly pg attach.
Moving on to Redis, let’s create an app using
fly apps create and call it
discourse-redis. We also need to create a volume for Redis to persist data in:
flyctl volumes create discourse_redis_disk --region maa -a discourse-redis
We can now create a
fly.toml based on the contents of https://github.com/fly-apps/redis/blob/main/fly.toml — and since this isn’t the main app in my working directory I’ll rename it to
redis-fly.toml, and update it with the correct app and volume names:
app = "discourse-redis" [[mounts]] destination = "/data" source = "discourse_redis_disk" [build] image = "flyio/redis:6.2.6"
We also need to set a Redis password (before the first deployment) with
fly secrets set REDIS_PASSWORD=OHSOSECRET -a discourse-redis
and then deploy specifically from the
redis-fly.toml file using
fly deploy -c redis-fly.toml
With our dependencies all set up, we can now get to deploying Discourse itself. Bitnami has a fully packaged Discourse container available, and by default two instances of the same image are run with different
CMDs — one for the web server and one to run Sidekiq. These two containers would need access to the same data volume because they operate on the same directory, but this means we can’t run them separately on Fly — if they need access to the same data volume they’ll need to be running on the same instance.
To make this happen we’ll use an idea described on the guide to running multiple processes, and use the hivemind process manager to run both the web server and Sidekiq simultaneously. We’ll extend
docker.io/bitnami/discourse:2 a bit with our own
FROM docker.io/bitnami/discourse:2 RUN curl -L https://github.com/DarthSim/hivemind/releases/download/v1.0.6/hivemind-v1.0.6-linux-amd64.gz -o hivemind.gz \ && gunzip hivemind.gz \ && mv hivemind /usr/local/bin COPY Procfile Procfile RUN chmod +x /usr/local/bin/hivemind ENTRYPOINT  CMD ["/usr/local/bin/hivemind"]
We’ll need to create the
Procfile referenced there as well. Since Bitnami uses an
ENTRYPOINT to properly bootstrap each script, we’ve cleared in the
Dockerfile and we’ll use it in the
web: /opt/bitnami/scripts/discourse/entrypoint.sh /opt/bitnami/scripts/discourse/run.sh sidekiq: /opt/bitnami/scripts/discourse/entrypoint.sh /opt/bitnami/scripts/discourse-sidekiq/run.sh
Let’s now create the app with
fly apps create — I’ll call it
discourse. And we’ll set up a simple
fly.toml with the internal port set at
3000 (that’s what this image uses):
app = "discourse" [[services]] internal_port = 3000 protocol = "tcp" [[services.ports]] handlers = ["http"] port = 80 [[services.ports]] handlers = ["tls", "http"] port = 443 [[mounts]] source = "discourse_data" destination = "/bitnami/discourse"
And add the data volume we’re referencing here with
fly volumes create discourse_data --region maa
Let’s now tell Fly to set up an application-specific database on the main DB instance with
fly pg attach --app discourse --postgres-app discourse-db
which should give us
Postgres cluster discourse-db is now attached to discourse The following secret was added to discourse: DATABASE_URL=postgres://discourse_g4k3rxgloy80qznj:firstname.lastname@example.org:5432/discourse?sslmode=disable
That’s everything we need to configure and launch our Discourse app. Let’s set up the following environment variables / secrets that we know are required based on https://github.com/bitnami/bitnami-docker-discourse/blob/master/README.md#user-and-site-configuration
DISCOURSE_HOST=discourse.fly.dev DISCOURSE_DATABASE_HOST=discourse-db.internal DISCOURSE_DATABASE_PORT_NUMBER=5432 DISCOURSE_DATABASE_USER=discourse_g4k3rxgloy80qznj DISCOURSE_DATABASE_PASSWORD=800548c36945dace5916a07b9d574a99 DISCOURSE_DATABASE_NAME=discourse DISCOURSE_REDIS_HOST=discourse-redis.internal DISCOURSE_REDIS_PORT_NUMBER=6379 DISCOURSE_REDIS_PASSWORD=OHSOSECRET
I’ll put these in a
.env file and set them all at one shot with
cat .env | fly secrets import
We’ll also need to give Discourse a little more RAM than the default 256MB, and since we’re running the web server and Sidekiq together let’s give it a safe 2GB with
fly scale memory 2048.
We can then deploy with
fly deploy, and watch the logs in another tab with
fly logs… and that’s it! Almost. Discourse also needs an SMTP server to do anything useful and some site configuration to look normal, but we have it running now, and can add these options as part of setting up the forum.