A short guide to running Bonfire in a production environment and setting up a digital space connected to the fediverse.
Bonfire is currently beta software. While it's fun to play with it, we would not recommend running any production instances yet (meaning not using it for your primary fediverse identity) because it's not quite ready for that today.
These instructions are for setting up Bonfire in production. If you want to run the backend in development, please refer to our Installation guide instead.
We recommend only granting an account to people you trust to minimise the attack surface. Accordingly, Bonfire ships with public registration disabled. The admin panel has an invite
facility.
Install using Co-op Cloud (recommended) which is an alternative to corporate cloud services built by tech co-ops, and provides handy tools for setting up and managing many self-hosted free software tools using ready-to-use "recipes". Very useful if you'd like to host Bonfire alongside other open and/or federated projects.
- Install Abra on your machine
- Set up a server with co-op cloud
- Use the Bonfire recipe and follow the instructions there, including editing the config in the env file at
~/.abra/servers/your_server/your_app.env
(see prepare the config for details about what to edit) - Run the abra deploy command and done!
- Install dependencies.
The easiest way to manage the docker image is using just commands.
The docker-compose.release.yml
uses config/prod/.env
to launch a container with the necessary environment variables along with its dependencies, currently that means an extra postgres container, along with a reverse proxy (Caddy server, which you may want to replace with nginx or whatever you prefer).
Make sure you have Docker, with the compose plugin, and just installed:
$ docker version
Docker Engine - Community - 23.0.1
$ docker compose version
Docker Compose version v2.16.0
$ just --version
just 1.13.0
...
- Clone this repository and change into the directory:
git clone --depth 1 https://github.com/bonfire-networks/bonfire-app.git bonfire && cd bonfire
- Specify what flavour you want to run in production:
The first thing to do is choose what flavour of Bonfire (eg. classic, community, or cooperation) you want to deploy, as each flavour uses different Docker images and set of configs. For example if you want to run the classic
flavour:
export MIX_ENV=prod FLAVOUR=classic WITH_DOCKER=yes
You may also want to put this in the appropriate place in your system so your choice of flavour is remembered for next time (eg. ~/.bashrc
or ~/.zshrc
)
- Run
just config
to initialise some default config and then edit the config in the./.env
file (see prepare the config for details about what to edit).
Now that your tooling is set up, you have the choice of using pre-built images or building your own. For example if your flavour does not have a prebuilt image on Docker Hub, or if you want to customise any of the extensions, you can build one yourself.
- The
image
entry indocker-compose.release.yml
will by default use the image on Docker Hub which corresponds to your chosen flavour (see step 1 above for choosing your flavour).
You can see the images available per flavour, version (we currently recommend using the latest
tag), and architecture at https://hub.docker.com/r/bonfirenetworks/bonfire/tags
- Try running the app!
Building your own Docker image is useful if you want to make code changes or add your own extensions.
Dockerfile.release
uses the multistage build feature to just the image as small as possible. It generates the OTP release which is later copied into the final image packaged in an Alpine linux container.
There is a justfile
with relevant commands (make sure set the MIX_ENV=prod
env variable):
just rel-build-locked
which builds the docker image of the latest releasejust rel-build
which builds the docker image, including local changes to any cloned extensions in./extensions/
just rel-tag
adds the "latest" tag to your last build, so that it will be used when running
Once you've built and tagged your image, you may need to update the image
name in docker-compose.release.release.yml
to match (either your local image name if running on the same machine you used for the build, or a remote image on Docker Hub if you pushed it) and then follow the same steps as for option A1.
For production, we recommend to set up a CI workflow to automate this, for an example you can look at the one we currently use.
Finally, try running the app!
#### Running with Docker
- Start the docker containers with docker-compose:
just rel-run
Run this at the prompt:
bin/bonfire remote
to enter Elixir's iex environment
Bonfire.Common.Repo.migrate
to initialise your database
The backend should now be running at http://localhost:4000/. Yay, you're up and running!
- If that worked, start the app as a daemon next time:
just rel-run-bg
(Alternatively, just rel-run-bg db
if you want to run the backend + db but not the web proxy, or just rel-run-bg db search
if you want to run the full-text search index.)
Running a custom build without Docker.
- Install dependencies.
- Postgres (or Postgis) version 12 or newer
- just
- Elixir version 1.15+ with OTP 25+ (see the
Dockerfile
to double check the versions we're currently using). If your distribution only has an old version available, check Elixir's install page or use a tool like mise (runmise install
in this directory) or asdf.
- Clone this repository and change into the directory:
git clone --depth 1 https://github.com/bonfire-networks/bonfire-app.git bonfire && cd bonfire
- Specify what flavour you want to run in production:
The first thing to do is choose what flavour of Bonfire (eg. classic, community, or cooperation) you want to deploy, as each flavour uses different Docker images and set of configs. For example if you want to run the classic
flavour:
export FLAVOUR=classic MIX_ENV=prod WITH_DOCKER=no
You may also want to put this in the appropriate place in your system so your choice of flavour is remembered for next time (eg. ~/.bashrc
or ~/.zshrc
)
-
Run
just config
to initialise some default config and then edit the config in the./.env
file (see prepare the config for details about what to edit). -
Run
just rel-build
to create an elixir release. This will create an executable in your_build/prod/rel/bonfire
directory. Note that you will needjust
to pass in the.env
file to the executable, like so:just cmd _build/prod/rel/bonfire/bin/bonfire <bonfire command>
. Alternatively, this file can be sourced bysource .env
instead. We will be using thebin/bonfire
executable as called fromjust
from here on. -
Running the release
-
Create a database, and a user, fill out the
.env
with your credentials and secrets -
You will need to use
just
in order to pass the.env
file to the executable. This can be accomplished by runningjust cmd _build/prod/rel/bonfire/bin/bonfire <bonfire command>
. Just works from the root directory of thejustfile
, not your current directory. -
If you’re using RDS or some other locked down DB, you may need to run
CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public;
on your database with elevated privileges. -
You can check if your instance is configured correctly and get to the iex console by running
bin/bonfire start
-
The migrations should automatically run on first boot, but if you run into troubles the migration command is:
Bonfire.Common.Repo.migrate()
in the iex console. -
To run the instance as a daemon, use
bin/bonfire start daemon
. Yay, you're up and running!
- Adding HTTPS
The common and convenient way for adding HTTPS is by using a reverse proxy like Nginx or Caddyserver (the latter of which is bundled as part of the docker compose setup).
Caddyserver and other servers can handle generating and setting up HTTPS certificates automatically, but if you need TLS/SSL certificates for nginx, you can look get some for free with letsencrypt. The simplest way to obtain and install a certificate is to use Certbot.. Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically.
If you've built from source, you should point the nginx root directory to be _build/prod/rel/bonfire/lib/bonfire-[current-version]/priv/static
This repo contains an experimental Flake and Nix module. These are not ready for production.
Here are the detailed steps to deploy it:
- run a recent version of Nix or NixOS: https://nixos.wiki
- enable Flakes: https://nixos.wiki/wiki/Flakes#Installing_flakes
- add
sandbox = false
in your nix.conf - fetch and build the app and dependencies:
nix run github:bonfire-networks/bonfire-app start_iex
- add it as an input to your system flake.
- add an overlay to just the package available
- add the required configuration in your system
Your flake.nix
file would look like the following. Remember to replace myHostName
with your actual hostname or however your deployed system is called.
{
inputs.bonfire.url = "github:bonfire-networks/bonfire-app/main";
outputs = { self, nixpkgs, bonfire }: {
overlay = final: prev: with final;{
# a package named bonfire already exists on nixpkgs so we put it under a different name
elixirBonfire = bonfire.packages.x86_64-linux.default;
};
nixosConfigurations.myHostName = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
{
environment.systemPackages = [ agenix.defaultPackage.x86_64-linux ];
nixpkgs.overlays = [ self.overlay ];
}
./myHostName.nix
bonfire.nixosModules.bonfire
];
};
};
}
Then your myHostName.nix
would look like the following:
{ config, lib, pkgs, ... }:
{
services.bonfire = {
# you will additionally need to expose bonfire with a reverse proxy, for example caddy
port = 4000;
package = pkgs.elixirBonfire;
dbName = "bonfire";
# the environment should contain a minimum of
# SECRET_KEY_BASE
# SIGNING_SALT
# ENCRYPTION_SALT
# RELEASE_COOKIE
# have a look into nix/module.nix for more details
# the way to deploy secrets is beyond this readme, but I would recommend agenix
environmentFile = "/run/secrets/bonfireEnv";
dbSocketDir = "/var/run/postgresql";
};
# this is specifically for a reverse proxy, which is primarily used for SSL certs
services.ngnix = {
enable = true;
forceSSL = true;
enableACME = true;
virtualHosts."myHostName".locations.proxyPass = "http://localhost:4000";
};
# You will need to accept ACME's terms and conditions if you haven't elsewhere in your configuration
security.acme.defaults.email = "[email protected]";
security.acme.acceptTerms = true;
# this is uniquely for database backup purposes
# replace myBackupUserName with the user name that will do the backups
# if you want to do backups differently, feel free to remove this part of the config
services.postgresql = {
ensureDatabases = [ "bonfire" ];
ensureUsers = [{
name = "myBackupUserName";
ensurePermissions = { "DATABASE bonfire" = "ALL PRIVILEGES"; };
}];
};
}
## Preparing the config (in .env)
The app needs these environment variables to be configured in order to work.
FLAVOUR
should reflect your chosen flavourHOSTNAME
(your domain name, eg:bonfire.example.com
)PUBLIC_PORT
(usually 443)MAIL_DOMAIN
andMAIL_KEY
and related keys to configure transactional email, for example setMAIL_BACKEND=mailgun
and sign up at Mailgun and then configure the domain name and key (you may also need to setMAIL_BASE_URI
if your domain is not setup in EU, as the defaultMAIL_BASE_URI
is set ashttps://api.eu.mailgun.net/v3
).- SMTP is supported as well, through the following env vars
MAIL_SERVER (smtp domain of the mail server)
MAIL_DOMAIN (the bit after the @ in your email)
MAIL_USER
MAIL_PASSWORD
MAIL_FROM
MAIL_PORT (optional)
MAIL_SSL (optional)
UPLOADS_S3_BUCKET
and the related API key and secret for uploads. WARNING: If you want to store uploads in an object storage rather than directly on your server (which you probably want, to not run out of space), you need to configure that up front, otherwise URLs will break if you change it later. Seeconfig/runtime.exs
for extra variables to set if you're not using the default service and region (which is Scaleway Paris).
You can run just secrets
to generate some for you.
SECRET_KEY_BASE
SIGNING_SALT
ENCRYPTION_SALT
POSTGRES_PASSWORD
MEILI_MASTER_KEY
In the ./config/
(which is a symbolic link to the config of the flavour you choose to run) directory of the codebase, there are following config files:
config.exs
: default base configuration, which itself loads many other config files, such as one for each installed Bonfire extension.prod.exs
: default extra configuration forMIX_ENV=prod
runtime.exs
: extra configuration which is loaded at runtime (vs the others which are only loaded once at compile time, i.e. when you build a release)bonfire_*.exs
: configs specific to different extensions, which are automatically imported byconfig.exs
You should not have to modify the files above. Instead, overload any settings from the above files using env variables or in ./.env
. If any settings in the .exs
config files are not available in env or in the instance settings UI, please open an issue or PR.
NOTE: If you are running in a restricted environment such as Amazon RDS, you will need to execute some sql against the database before migrations can run:
CREATE EXTENSION IF NOT EXISTS citext;
By default, the backend listens on port 4000 (TCP), so you can access it on http://localhost:4000/ (if you are on the same machine). In case of an error it will restart automatically.
Once you've signed up, you will automatically be an instance admin if you were the first to register.
You can sign up via CLI by entering something like this in your app's Elixir console:
Bonfire.Me.make_account_only("[email protected]", "my pw")
just update
to update to the latest release of Bonfirejust rel-run
Run the app in Docker, in the foregroundjust rel-run-bg
Run the app in Docker, and keep running in the backgroundjust rel-stop
Stop the running releasejust rel-shell
Runs a simple shell inside of the container, useful to explore the image
Once in the shell, you can run bin/bonfire
with the following commands:
Usage: bonfire COMMAND [ARGS]
The known commands are:
start
Starts the systemstart_iex
Starts the system with IEx attacheddaemon
Starts the system as a daemondaemon_iex
Starts the system as a daemon with IEx attachedeval "EXPR"
Executes the given expression on a new, non-booted systemrpc "EXPR"
Executes the given expression remotely on the running systemremote
Connects to the running system via a IEx remote shellrestart
Restarts the running system via a remote commandstop
Stops the running system via a remote commandpid
Prints the operating system PID of the running system via a remote commandversion
Prints the release name and version to be booted
There are some useful database-related release tasks under EctoSparkles.Migrator.
that can be run in an iex
console (which you get to with just rel.shell
followed by bin/bonfire remote
, assuming the app is already running):
migrate
runs all up migrationsrollback(step)
roll back to step Xrollback_to(version)
roll back to a specific versionrollback_all
rolls back all migrations back to zero (caution: this means losing all data)
You can also directly call some functions in the code from the command line, for example:
- to migrate:
docker exec bonfire_web bin/bonfire rpc 'Bonfire.Common.Repo.migrate'
- to just yourself an admin:
docker exec bonfire_web bin/bonfire rpc 'Bonfire.Me.Users.make_admin("my_username")'
- LiveDashboard for viewing real-time metrics and logs at
/admin/system/
- Oban logs for viewing queued jobs (e.g. for processing federated activities)
/admin/system/oban_queues
- LiveAdmin for browsing data in the database at
/admin/system/data
- Orion for dynamic distributed performance profiling at
/admin/system/orion
- Web Observer as an alternative way to view metrics at
/admin/system/wobserver
Some common issues that may arise during deployment and our suggestions for resolving them.
If you are running Bonfire behind your own reverse proxy (e.g. nginx), you might experience issues with WebSocket connections not establishing. WebSocket connections require specific configuration to work, in nginx the following configuration is necessary for websockets to work:
location /live/websocket {
proxy_pass http://127.0.0.1:4000;
# these configurations are necessary to proxy WebSocket requests
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}