Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a007714803 | ||
|
|
863538311d | ||
|
|
5dc3d86de0 | ||
|
|
85201a8fa7 | ||
|
|
fd6280026a | ||
|
|
09383b8f89 | ||
|
|
90f5ff3fbb | ||
|
|
29bf8a4dfa | ||
|
|
d9b24e61a7 | ||
|
|
c30b8cd57a | ||
|
|
f771ba08f3 | ||
|
|
398f3c2bbb | ||
|
|
48e7e26bb0 | ||
|
|
a49cdecfd8 | ||
|
|
576f2e2dda | ||
|
|
ead44b7103 |
+14
@@ -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=
|
||||
@@ -173,6 +180,13 @@ UPLOAD_LIMIT=20000000
|
||||
#SECRET_HONEYCOMB_API_KEY_VERSION=v1
|
||||
#SECRET_LIGHTSTEP_API_KEY_VERSION=v1
|
||||
|
||||
# ====================================
|
||||
# PLAUSIBLE ANALYTICS
|
||||
|
||||
#PLAUSIBLE_PROXY_ENABLED=true
|
||||
#PLAUSIBLE_PROXY_DOMAIN=https://plausible.example.com
|
||||
#PLAUSIBLE_PROXY_SCRIPT_PATH=/js/script.js # optional
|
||||
|
||||
# ====================================
|
||||
# BONFIRE EXTENSIONS AND MISC
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ If you want to use bleeding edge releases try setting `APP_VERSION=latest-alpha`
|
||||
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 <app-name>`
|
||||
|
||||
## Serve uploads with nginx and show a maintenance page
|
||||
|
||||
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,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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -15,7 +15,7 @@ services:
|
||||
# - sonic_password
|
||||
|
||||
search:
|
||||
image: valeriansaliou/sonic:v1.7.4
|
||||
image: valeriansaliou/sonic:v1.8.1
|
||||
# secrets:
|
||||
# - sonic_password
|
||||
volumes:
|
||||
|
||||
@@ -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
|
||||
+17
-2
@@ -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
|
||||
@@ -56,6 +58,10 @@ services:
|
||||
- LIVEBOOK_PASSWORD_FILE=/run/secrets/livebook_password
|
||||
- RELEASE_COOKIE_FILE=/run/secrets/release_cookie
|
||||
|
||||
- PLAUSIBLE_PROXY_ENABLED
|
||||
- PLAUSIBLE_PROXY_DOMAIN
|
||||
- PLAUSIBLE_PROXY_SCRIPT_PATH
|
||||
|
||||
secrets:
|
||||
- secret_key_base
|
||||
- signing_salt
|
||||
@@ -69,6 +75,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 +94,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
|
||||
|
||||
@@ -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>
|
||||
@@ -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}
|
||||
|
||||
@@ -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}')"
|
||||
|
||||
@@ -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.
|
||||
@@ -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/"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user