Author SHA1 Message Date
mayel 863538311d avoid postgres config clobber 2026-10-22 15:42:13 +01:00
mayel 5dc3d86de0 add optional nginx server for uploads + maintanance page 2026-10-21 09:33:54 +01:00
mayel 85201a8fa7 add csp for uploads 2026-10-04 14:16:24 +01:00
mayel fd6280026a add env 2026-10-02 21:20:42 +01:00
mayel 09383b8f89 update sonic config 2026-09-10 10:39:55 +01:00
stevensting 90f5ff3fbb chore: publish 2.0.2+1.0.7-social-amd64 release 2026-09-02 09:37:19 +02:00
stevensting 29bf8a4dfa increase bonfire version to 1.0.7 2026-09-02 09:34:45 +02:00
stevensting d9b24e61a7 add conf file versions to get rid of warnings during deploy 2026-09-02 09:33:21 +02:00
renovate-bot c30b8cd57a chore(deps): update valeriansaliou/sonic docker tag to v1.8.1 (#20)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [valeriansaliou/sonic](https://github.com/valeriansaliou/sonic) | minor | `v1.7.4` -> `v1.8.1` |

> ❗ **Important**
>
> Release Notes retrieval for this PR were skipped because no github.com credentials were available.
> If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/examples/self-hosting.md#githubcom-token-for-release-notes).

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNzMuMSIsInVwZGF0ZWRJblZlciI6IjQxLjE3My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: #20
Co-authored-by: Renovate Bot <renovate@coopcloud.tech>
2026-09-02 07:07:34 +00:00
mayel f771ba08f3 Merge pull request 'add BONFIRE_CONSOLE_TOKEN' (#21) from console-token into main
Reviewed-on: #21
2026-08-26 13:47:31 +00:00
stevensting 398f3c2bbb add env var 2026-08-18 16:34:16 +02:00
mayel 48e7e26bb0 Merge pull request 'update documentation' (#19) from more-doc into main
Reviewed-on: #19
2026-08-05 09:04:08 +00:00
stevensting 919455212a refine admin account creation 2026-08-05 08:50:24 +00:00
stevensting a49cdecfd8 chore: publish 2.0.1+1.0.6-social-amd64 release 2026-08-04 16:03:16 +02:00
stevensting 576f2e2dda increase bonfire version to 1.0.6 2026-08-04 16:00:27 +02:00
stevensting ead44b7103 chore: publish 2.0.0+1.0.5-social-amd64 release 2026-08-04 15:56:10 +02:00
stevensting c00d5b4a16 add documentation 2026-08-04 15:51:30 +02:00
mayel 4aacfaa0ee Merge pull request 'remove meilisearch' (#18) from remove-meilisearch into main
Reviewed-on: #18
2026-08-04 12:46:07 +00:00
14 changed files with 205 additions and 27 deletions
+7
View File
@@ -63,6 +63,12 @@ DB_MIGRATE_INDEXES_CONCURRENTLY=false
# docker secret cannot be used due to distroless image, threfore add secret here
#SONIC_PASSWORD=
# ====================================
# STATIC FILES & MAINTENANCE PAGE
## serve uploads with nginx (bypassing Phoenix) and show a maintenance page while the app restarts. NOTE: don't combine with BASIC_AUTH, as uploads would bypass it
#COMPOSE_FILE="$COMPOSE_FILE:compose.static.yml"
# ====================================
# MAIL
@@ -122,6 +128,7 @@ UPLOAD_LIMIT=20000000
# OpenID Connect: connect as a client to the OpenID Connect provider with callback url https://yourinstance.tld/openid/client/openid_1
#COMPOSE_FILE="$COMPOSE_FILE:compose.auth.openid.yml"
#SECRET_OPENID_CLIENT_SECRET_VERSION=v1
# OPENID_1_ISSUER=
# OPENID_1_DISCOVERY=
# OPENID_1_DISPLAY_NAME=
# OPENID_1_CLIENT_ID=
+33 -13
View File
@@ -15,24 +15,42 @@ A [coop-cloud](https://coopcloud.tech) recipe for deploying [Bonfire](https://bo
## Basic usage
1. Set up Docker Swarm and [`abra`]
2. Deploy [`coop-cloud/traefik`]
3. `abra app new bonfire --secrets` (optionally with `--pass` if you'd like to save secrets in `pass`) and select your server from the list and enter the domain name you want Bonfire to be served from
4. `abra app config YOUR_APP_DOMAIN_NAME` and check/edit the config keys
5. `abra app deploy YOUR_APP_DOMAIN_NAME`
6. Open the configured domain in your browser and sign up!
1. `abra app new bonfire --secrets`
2. `abra app config <app-name>`
3. `abra app deploy <app-name>`
4. Open the Elixir console: `abra app run <app-name> app bin/bonfire remote`
5. Create admin account in Elixir console (the first account created this way will get admin rights): `Bonfire.Me.make_account_only("my@email.net", "my pw")`
## Upgrades
`abra app deploy --force your-server.domain.name`
## Use a custom Bonfire flavour
NOTE: we recommend switching to the new `sonic` search backend (instead of the deprecated `meilisearch`):
1. comment the `COMPOSE_FILE` line that contains `compose.meilisearch.yml` in the `.env` file for your bonfire instancem and replace with a line like `COMPOSE_FILE="compose.yml:compose.sonic.yml"`
By default, this recipe deploys the Bonfire `social` flavour. If you want a different one uncomment and define `APP_FLAVOUR` in you env file.
## Use a custom Bonfire version
Attention: This is not recommended for production deployment!
You can deploy another version of Bonfire by using `APP_VERSION` and `APP_PLATFORM`. Be aware that not all versions might be compatible with this recipe, especially the quite old ones.
If you want to use bleeding edge releases try setting `APP_VERSION=latest-alpha`. When using this, every time you deploy with `abra app deploy --force <app-name>` the latest alpha replease will be used.
## Using a search backend
1. uncomment the `COMPOSE_FILE` line that contains `compose.sonic.yml` in the `.env` file for your bonfire instance.
2. add `SONIC_PASSWORD=a-super-secret-password` to the same file (make sure to change the password after pasting!)
3. redeploy with `abra app deploy --force your-server.domain.name`
3. redeploy with `abra app deploy --force <app-name>`
## Serve uploads with nginx and show a maintenance page
[`abra`]: https://docs.coopcloud.tech/abra/
[`coop-cloud/traefik`]: https://git.coopcloud.tech/coop-cloud/traefik
An optional nginx service serves uploaded files straight from the uploads volume, so they don't go through the app, and keep working while it restarts. It also shows a maintenance page whenever the app is down, such as during upgrades. Uncomment the `COMPOSE_FILE` line that contains `compose.static.yml` in the `.env` file for your bonfire instance to enable.
## Protect the instance with basic auth
For e.g. a testing phase you can protect your Bonfire instance with basic auth:
1. uncomment the section `## BASIC_AUTH`
2. create a username/password combination with `echo $(htpasswd -nB <username>)`
3. add the combination as secret with `abra app secret insert <app-name> usersfile v1 '<the-user-password-combination>'`
##
## FAQ
@@ -44,3 +62,5 @@ Go into your app's Elixir console and enter something like `Bonfire.Me.make_acco
### How can I get to the app's Elixir console?
`abra app run your-server.domain.name app bin/bonfire remote`
For more, see [docs.coopcloud.tech](https://docs.coopcloud.tech).
+7 -1
View File
@@ -1,5 +1,11 @@
export APP_ENTRYPOINT_VERSION=v4
export PG_BACKUP_VERSION=v6
export MEILI_BACKUP_VERSION=v4
export SONIC_CFG_VERSION=v1
export SONIC_CFG_VERSION=v2
export SONIC_ENTRYPOINT_VERSION=v1
export POSTGRES_ENTRYPOINT_VERSION=v7
export POSTGRES_TUNE_VERSION=v2
export POSTGRES_CONF_TMPL_VERSION=v3
export BASH_ENVSUBST_VERSION=v1
export STATIC_NGINX_CONF_VERSION=v1
export MAINTENANCE_HTML_VERSION=v1
+1
View File
@@ -4,6 +4,7 @@ services:
app:
environment:
- OPENID_1_DISPLAY_NAME
- OPENID_1_ISSUER
- OPENID_1_DISCOVERY
- OPENID_1_CLIENT_ID
- OPENID_1_CLIENT_SECRET_FILE=/run/secrets/openid_client_secret
+2
View File
@@ -7,6 +7,8 @@ services:
labels:
- "traefik.http.middlewares.${STACK_NAME}_basicauth.basicauth.usersFile=/run/secrets/usersfile"
- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}_basicauth"
# the uploads router (see compose.yml) needs the basic auth too, or uploads would bypass it
- "traefik.http.routers.${STACK_NAME}_uploads.middlewares=${STACK_NAME}_basicauth,${STACK_NAME}_uploads_csp"
secrets:
usersfile:
+1 -1
View File
@@ -15,7 +15,7 @@ services:
# - sonic_password
search:
image: valeriansaliou/sonic:v1.7.4
image: valeriansaliou/sonic:v1.8.1
# secrets:
# - sonic_password
volumes:
+53
View File
@@ -0,0 +1,53 @@
---
version: "3.8"
# Opt-in nginx sidecar that serves uploads directly from the volume (bypassing Phoenix) and shows a maintenance page whenever the app has no running task (e.g. during redeploys and upgrades). NOTE: don't combine with `compose.basicauth.yml`, as uploads served from here would bypass the basic auth.
services:
static:
image: nginx:1.29.0-alpine
networks:
- proxy
volumes:
# NOTE: the app and this service share the local volume, so they must run on the same node
- upload-data:/srv/data/uploads:ro
configs:
- source: static_nginx_conf
target: /etc/nginx/conf.d/default.conf
mode: 0444
- source: maintenance_html
target: /srv/maintenance/maintenance.html
mode: 0444
healthcheck:
# 127.0.0.1 rather than localhost, as nginx only listens on IPv4
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/maintenance.html"]
interval: 30s
timeout: 10s
retries: 10
start_period: 5s
deploy:
restart_policy:
condition: on-failure
labels:
- "traefik.enable=true"
- "traefik.http.services.${STACK_NAME}_static.loadbalancer.server.port=80"
# serve uploads from here rather than through the app's `_uploads` router (which has the same rule, so this one needs an explicit higher priority)
- "traefik.http.routers.${STACK_NAME}_static_uploads.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS}) && PathPrefix(`/data/uploads/`)"
- "traefik.http.routers.${STACK_NAME}_static_uploads.priority=10000"
- "traefik.http.routers.${STACK_NAME}_static_uploads.entrypoints=web-secure"
- "traefik.http.routers.${STACK_NAME}_static_uploads.tls.certresolver=${LETS_ENCRYPT_ENV}"
- "traefik.http.routers.${STACK_NAME}_static_uploads.service=${STACK_NAME}_static"
# lowest priority fallback: Traefik drops the app's routers while it has no running task, so requests land here and get the maintenance page
- "traefik.http.routers.${STACK_NAME}_maintenance.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})"
- "traefik.http.routers.${STACK_NAME}_maintenance.priority=1"
- "traefik.http.routers.${STACK_NAME}_maintenance.entrypoints=web-secure"
- "traefik.http.routers.${STACK_NAME}_maintenance.tls.certresolver=${LETS_ENCRYPT_ENV}"
- "traefik.http.routers.${STACK_NAME}_maintenance.service=${STACK_NAME}_static"
configs:
static_nginx_conf:
name: ${STACK_NAME}_static_nginx_conf_${STATIC_NGINX_CONF_VERSION:-v1}
file: static.nginx.conf
maintenance_html:
name: ${STACK_NAME}_maintenance_html_${MAINTENANCE_HTML_VERSION:-v1}
file: maintenance.html
+13 -2
View File
@@ -3,7 +3,7 @@ version: "3.8"
services:
app:
image: ${CONTAINER_REGISTRY:-bonfirenetworks/bonfire}:${APP_VERSION:-1.0.5}-${APP_FLAVOUR:-social}-${APP_PLATFORM:-amd64}
image: ${CONTAINER_REGISTRY:-bonfirenetworks/bonfire}:${APP_VERSION:-1.0.7}-${APP_FLAVOUR:-social}-${APP_PLATFORM:-amd64}
logging:
driver: "json-file"
options:
@@ -49,6 +49,8 @@ services:
- ENABLE_SSO_PROVIDER
- OAUTH_ISSUER
- BONFIRE_CONSOLE_TOKEN
- SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base
- SIGNING_SALT_FILE=/run/secrets/signing_salt
- ENCRYPTION_SALT_FILE=/run/secrets/encryption_salt
@@ -69,6 +71,8 @@ services:
- proxy
- internal
entrypoint: ["/docker-entrypoint.sh", "./bin/bonfire", "start"]
# give the app time to shut down cleanly: on SIGTERM Oban stops fetching jobs and waits up to its `shutdown_grace_period` (default 15s) for running ones to finish, then the endpoint drains LiveView sockets and waits up to Thousand Island's `shutdown_timeout` (default 15s) for HTTP requests. These run one after the other, but the Swarm default (10s) force-kills the app first on every redeploy. Keep this above their sum.
stop_grace_period: 42s
configs:
- source: app_entrypoint
target: /docker-entrypoint.sh
@@ -86,7 +90,14 @@ services:
- "traefik.http.routers.${STACK_NAME}.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS})"
- "traefik.http.routers.${STACK_NAME}.entrypoints=web-secure"
- "traefik.http.routers.${STACK_NAME}.tls.certresolver=${LETS_ENCRYPT_ENV}"
- "coop-cloud.${STACK_NAME}.version=1.0.0+1.0.5-social-amd64"
# uploads (e.g. SVG) are served from the instance's origin, so stop any script in them from running if opened directly
- "traefik.http.middlewares.${STACK_NAME}_uploads_csp.headers.customresponseheaders.Content-Security-Policy=sandbox; default-src 'none'; style-src 'unsafe-inline'"
- "traefik.http.routers.${STACK_NAME}_uploads.rule=Host(`${DOMAIN}`${EXTRA_DOMAINS}) && PathPrefix(`/data/uploads/`)"
- "traefik.http.routers.${STACK_NAME}_uploads.entrypoints=web-secure"
- "traefik.http.routers.${STACK_NAME}_uploads.tls.certresolver=${LETS_ENCRYPT_ENV}"
- "traefik.http.routers.${STACK_NAME}_uploads.service=${STACK_NAME}"
- "traefik.http.routers.${STACK_NAME}_uploads.middlewares=${STACK_NAME}_uploads_csp"
- "coop-cloud.${STACK_NAME}.version=2.0.3+1.0.7-social-amd64"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4000"]
interval: 30s
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="30">
<title>Back in a moment</title>
<style>
body { margin: 0; min-height: 100vh; display: grid; place-items: center; font-family: system-ui, sans-serif; background: #1d1f21; color: #e5e5e5; text-align: center; }
main { max-width: 32rem; padding: 2rem; }
h1 { font-size: 1.5rem; }
p { line-height: 1.5; opacity: 0.8; }
</style>
</head>
<body>
<main>
<h1>Back in a moment</h1>
<p>This site is offline, possibly restarting for an upgrade.</p>
<p>Will try again in a few seconds...</p>
</main>
</body>
</html>
+1 -1
View File
@@ -39,7 +39,7 @@ services:
configs:
postgres_entrypoint:
name: ${STACK_NAME}_postgres_entrypoint_${POSTGRES_ENTRYPOINT_VERSION:-v6}
name: ${STACK_NAME}_postgres_entrypoint_${POSTGRES_ENTRYPOINT_VERSION:-v7}
file: postgres/postgres-entrypoint.sh
postgres_tune:
name: ${STACK_NAME}_postgres_tune_${POSTGRES_TUNE_VERSION:-v2}
+30 -4
View File
@@ -8,8 +8,12 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[[ "$SCRIPT_DIR" == "/" ]] && SCRIPT_DIR=""
# Configuration via environment variables
POSTGRES_CONF_PATH="${POSTGRES_CONF_PATH:-/var/lib/postgresql/data/postgresql.auto.conf}"
POSTGRES_START_CMD="${POSTGRES_START_CMD:-docker-entrypoint.sh postgres}"
# The tuned config is a standalone config_file OUTSIDE the data dir, so it never shares postgresql.auto.conf with `ALTER SYSTEM`. Bonfire's InstanceTuning clears its own ALTER SYSTEM writes on every app boot (`ALTER SYSTEM RESET`), which wiped the tuner's values when both lived in auto.conf. Now RESET falls back to this file, and admin ALTER SYSTEM values survive restarts. Outside the data dir also keeps a fresh volume empty for initdb.
# POSTGRES_CONF_PATH="${POSTGRES_CONF_PATH:-/var/lib/postgresql/data/postgresql.auto.conf}"
POSTGRES_CONF_PATH="${POSTGRES_CONF_PATH:-/etc/postgresql/bonfire.conf}"
# NOT `-c name=value` per setting: command-line values outrank postgresql.auto.conf and would silently disable InstanceTuning
POSTGRES_START_CMD="${POSTGRES_START_CMD:-docker-entrypoint.sh postgres -c config_file=${POSTGRES_CONF_PATH}}"
PGDATA="${PGDATA:-/var/lib/postgresql/data}"
# Check if envsubst is available, if not, load our bash implementation
if ! command -v envsubst &> /dev/null; then
@@ -45,10 +49,32 @@ if mkdir -p "${CONF_DIR}" 2>/dev/null; then
# Write the processed config
echo "$config" > "${POSTGRES_CONF_PATH}"
echo "PostgreSQL: Configuration generated at ${POSTGRES_CONF_PATH}"
# One-time cleanup for volumes created when this script wrote the tuned config INTO postgresql.auto.conf: those stale lines override the config_file above (and shared_buffers, max_connections etc. are not InstanceTuning knobs, so nothing else would ever clear them). Runs once per volume (marker file), so later admin ALTER SYSTEM values persist.
AUTO_CONF="${PGDATA}/postgresql.auto.conf"
TUNE_MARKER="${PGDATA}/.bonfire_tune_v2"
if [[ -f "${PGDATA}/PG_VERSION" && -f "${AUTO_CONF}" && ! -f "${TUNE_MARKER}" ]]; then
echo "PostgreSQL: removing keys the tuner now sets from ${AUTO_CONF} (one-time migration)"
tmp_conf="$(mktemp)"
# first input = the generated config (collect its keys), second = auto.conf (drop those keys, keep the rest)
awk 'NR==FNR { k=$0; sub(/[[:space:]]*=.*/, "", k); keys[k]=1; next }
{ k=$0; sub(/[[:space:]]*=.*/, "", k); if (!(k in keys)) print }' \
<(echo "$config") "${AUTO_CONF}" > "${tmp_conf}"
# write in place (not mv) so the file keeps its postgres ownership
cat "${tmp_conf}" > "${AUTO_CONF}"
rm -f "${tmp_conf}"
touch "${TUNE_MARKER}"
elif [[ ! -f "${PGDATA}/PG_VERSION" && ! -f "${TUNE_MARKER}" ]]; then
# Fresh volume: nothing to clean, but the marker can't be written yet (initdb needs an empty data dir). Write it once initdb is done, or the next boot would wrongly run the cleanup and drop admin ALTER SYSTEM values. The loop survives the exec below as an orphan and gives up after 10 minutes.
( for _ in $(seq 1 600); do
if [[ -f "${PGDATA}/PG_VERSION" ]]; then touch "${TUNE_MARKER}"; break; fi
sleep 1
done ) >/dev/null 2>&1 &
fi
# Check if we should start postgres
if [[ -n "${POSTGRES_START_CMD}" && "${POSTGRES_START_CMD}" != "none" ]]; then
# Start PostgreSQL - postgresql.auto.conf is automatically loaded from data dir
# Start PostgreSQL with the tuned config_file. postgresql.auto.conf (ALTER SYSTEM) is still loaded from the data dir and layered on top
exec ${POSTGRES_START_CMD}
else
echo "PostgreSQL: Skipping startup (POSTGRES_START_CMD is '${POSTGRES_START_CMD}')"
+1
View File
@@ -0,0 +1 @@
Mailisearch search backend was removed. If you want to use a search backend switch to sonic. See readme on how to use it.
+7 -5
View File
@@ -6,13 +6,15 @@
[channel]
inet = "0.0.0.0:1491"
# keep in sync with :keepalive_interval in Bonfire.Search.Sonic, which must stay under this or Sonic drops our idle ingest/search connections
tcp_timeout = 300
[channel.search]
query_limit_default = 10
query_limit_maximum = 200
suggest_limit_default = 5
suggest_limit_maximum = 20
# moved out of [channel.search], deprecated since Sonic 1.8
[search]
query_limit_default = 10
query_limit_maximum = 200
suggest_limit_default = 5
suggest_limit_maximum = 20
[store.kv]
path = "/var/lib/sonic/store/kv/"
+27
View File
@@ -0,0 +1,27 @@
# used by compose.static.yml
server {
listen 80;
server_tokens off;
root /srv;
# mirrors the uploads `Plug.Static` in Bonfire's endpoint
location /data/uploads/ {
# uploads (e.g. SVG) are served from the instance's origin, so stop any script in them from running if opened directly
add_header Content-Security-Policy "sandbox; default-src 'none'; style-src 'unsafe-inline'" always;
gzip_static on;
try_files $uri =404;
}
# anything else only reaches this service while the app is down (see the maintenance router in compose.static.yml)
location / {
return 503;
}
error_page 503 /maintenance.html;
location = /maintenance.html {
root /srv/maintenance;
add_header Retry-After 30 always;
add_header Cache-Control "no-store" always;
}
}