Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48e7e26bb0 | |||
| 919455212a | |||
| a49cdecfd8 | |||
| 576f2e2dda | |||
| ead44b7103 | |||
| c00d5b4a16 | |||
| 4aacfaa0ee | |||
| 02ec8ba161 | |||
| fb0b664373 | |||
| 79a43dbadc | |||
| 466ff8e447 | |||
| d74f05c0d1 | |||
| df3229884d | |||
| 87f9209cd5 | |||
| 8c4d9cce46 | |||
| 5a183a0579 | |||
| 197c18a9b2 | |||
| 21a00c23c3 | |||
| f1499d6fda | |||
| 061a986d72 | |||
| 15b48c6033 | |||
| d3d5973ff6 | |||
| d6ce1547c9 | |||
| 7747970d1c | |||
| 6f1dd8db55 | |||
| f278be788c | |||
| 63ba064930 | |||
| 6588ce03ea | |||
| 38cbbf770c | |||
| 9082523712 | |||
| 6c5e6c64ea | |||
| 257918125f | |||
| a81c2123d1 |
+133
-88
@@ -1,34 +1,13 @@
|
||||
TYPE=bonfire
|
||||
|
||||
# choose what flavour of Bonfire to run
|
||||
FLAVOUR=social
|
||||
APP_VERSION=latest
|
||||
|
||||
# choose what extra services you want to run
|
||||
COMPOSE_FILE="compose.yml:compose.meilisearch.yml"
|
||||
# Enable this one for Postgres tuning (recommended, but only once your instance already is deployed and working):
|
||||
# COMPOSE_FILE="compose.yml:postgres/compose.postgres.tune.yml:compose.meilisearch.yml"
|
||||
|
||||
APP_VERSION_FLAVOUR=${APP_VERSION}-${FLAVOUR}
|
||||
# Different flavours/forks or architectures may require different builds of bonfire:
|
||||
# for ARM (manual build):
|
||||
# APP_DOCKER_IMAGE=bonfirenetworks/bonfire:${APP_VERSION_FLAVOUR}-aarch64
|
||||
# for x86 (built by CI):
|
||||
APP_DOCKER_IMAGE=bonfirenetworks/bonfire:${APP_VERSION_FLAVOUR}-amd64
|
||||
# multi-arch image (built by CI, but currently not working):
|
||||
#APP_DOCKER_IMAGE=bonfirenetworks/bonfire:${APP_VERSION_FLAVOUR}
|
||||
|
||||
DB_DOCKER_VERSION=17-3.5
|
||||
# note that different flavours or architectures may require different postgres builds:
|
||||
# For ARM or x86:
|
||||
DB_DOCKER_IMAGE=ghcr.io/baosystems/postgis:${DB_DOCKER_VERSION}
|
||||
# for x86:
|
||||
# DB_DOCKER_IMAGE=postgis/postgis:${DB_DOCKER_VERSION}-alpine
|
||||
# multiarch (but doesn't have required Postgis extension)
|
||||
#DB_DOCKER_IMAGE=postgres:${DB_DOCKER_VERSION}-alpine
|
||||
# TODO: maybe to use for Upgrading to a new Postgres version? (NOTE: does not work with postgis data)
|
||||
# DB_DOCKER_IMAGE=pgautoupgrade/pgautoupgrade:16-alpine
|
||||
LETS_ENCRYPT_ENV=production
|
||||
|
||||
# choose what flavour of Bonfire to run. default: social
|
||||
#APP_FLAVOUR=social
|
||||
# uncomment to run specific version e.g. 1.0.4 or latest release branch. available: latest, latest-rc, latest-beta, latest-alpha
|
||||
#APP_VERSION=latest
|
||||
# choose specific build. default: amd64, available: aarch64
|
||||
#APP_PLATFORM=aarch64
|
||||
|
||||
# enter your instance's domain name
|
||||
DOMAIN=bonfire.example.com
|
||||
@@ -39,41 +18,67 @@ DOMAIN=bonfire.example.com
|
||||
# enable abra backups
|
||||
ENABLE_BACKUPS=true
|
||||
|
||||
COMPOSE_FILE="compose.yml"
|
||||
|
||||
# uncomment in order to NOT automatically change the database schema when you upgrade the app
|
||||
# DISABLE_DB_AUTOMIGRATION=true
|
||||
# ====================================
|
||||
# DATABASE CONFIG
|
||||
|
||||
# max file upload size - default is 20 meg
|
||||
UPLOAD_LIMIT=20000000
|
||||
COMPOSE_FILE="$COMPOSE_FILE:compose.postgres.yml"
|
||||
SECRET_POSTGRES_PASSWORD_VERSION=v1
|
||||
|
||||
# upper limit for how much RAM you want the DB to use, in megabytes (the same amount of swap will also be allocated to the DB container)
|
||||
DB_MEMORY_LIMIT=5000
|
||||
# upper limit for how much RAM you want the DB to use, in megabytes (the same amount of swap will also be allocated to the DB container) As a rule of thumb use 25% of system RAM.
|
||||
DB_MEMORY_LIMIT=1024
|
||||
|
||||
#DB_MAX_CONNECTIONS=200
|
||||
|
||||
# set to true *after* your initial deployment to enable concurrent index creation for faster migrations in future upgrades
|
||||
DB_MIGRATE_INDEXES_CONCURRENTLY=false
|
||||
|
||||
# how much info to include in logs (from less to more: emergency, alert, critical, error, warning, notice, info, debug)
|
||||
LOG_LEVEL=info
|
||||
# for manual selection of DB version and docker image uncomment
|
||||
#DB_DOCKER_VERSION=17-3.5
|
||||
# note that different flavours or architectures may require different postgres builds:
|
||||
# For ARM or x86:
|
||||
#DB_DOCKER_IMAGE=ghcr.io/baosystems/postgis
|
||||
# for x86:
|
||||
#DB_DOCKER_IMAGE=postgis/postgis
|
||||
#DB_DOCKER_VERSION=17-3.5-alpine
|
||||
|
||||
# uncomment in order to NOT automatically change the database schema when you upgrade the app
|
||||
# DISABLE_DB_AUTOMIGRATION=true
|
||||
|
||||
# Enable `compose.postgres.tune.yml` for Postgres tuning (can only be enabled *after* your instance already is deployed and working)
|
||||
# COMPOSE_FILE="$COMPOSE_FILE:postgres/compose.postgres.tune.yml"
|
||||
|
||||
# ====================================
|
||||
# SECRETS
|
||||
## BASIC_AUTH
|
||||
|
||||
# please make sure you change everything to your own secrets!
|
||||
# and do not check your env file into any public git repo
|
||||
# change ALL the values:
|
||||
## Use 'echo $(htpasswd -nB username)' to generate the secret and store it in secret 'usersfile', multiple user/hashedpassword combinations can be added as comma separated list
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.basicauth.yml"
|
||||
#SECRET_USERSFILE_VERSION=v1
|
||||
|
||||
# what service to use for sending out emails (eg. smtp, mailgun, none) NOTE: you should also set the corresponding keys in secrets.env
|
||||
MAIL_BACKEND=none
|
||||
# ====================================
|
||||
# SEARCH BACKEND
|
||||
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.mail.yml"
|
||||
#SECRET_MAIL_PASSWORD_VERSION=v1
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.sonic.yml"
|
||||
# docker secret cannot be used due to distroless image, threfore add secret here
|
||||
#SONIC_PASSWORD=
|
||||
|
||||
# ====================================
|
||||
# MAIL
|
||||
|
||||
# COMPOSE_FILE="$COMPOSE_FILE:compose.mail.yml"
|
||||
# SECRET_MAIL_KEY_VERSION=v1
|
||||
# private key only necessary for some mail provider
|
||||
# COMPOSE_FILE="$COMPOSE_FILE:compose.mail.privatekey.yml"
|
||||
# SECRET_MAIL_PRIVATE_KEY_VERSION=v1
|
||||
|
||||
# what service to use for sending out emails (eg. smtp, mailgun, none) NOTE: you should also set the corresponding keys below for the relevant service you choose, and uncomment the COMPOSE_FILE line for the relevant service if needed
|
||||
#MAIL_BACKEND=none
|
||||
|
||||
# signup to an email service and edit with relevant info, see: https://docs.bonfirenetworks.org/Bonfire.Mailer.html
|
||||
# MAIL_DOMAIN=mgo.example.com
|
||||
# MAIL_KEY=xyz
|
||||
# MAIL_FROM=admin@example.com
|
||||
# MAIL_PROJECT_ID=
|
||||
# MAIL_PRIVATE_KEY=
|
||||
# MAIL_BASE_URI=
|
||||
# MAIL_REGION=
|
||||
# MAIL_SESSION_TOKEN=
|
||||
@@ -86,85 +91,125 @@ MAIL_BACKEND=none
|
||||
# MAIL_RETRIES=
|
||||
# MAIL_ARGS=
|
||||
|
||||
# ====================================
|
||||
# UPLOADS
|
||||
|
||||
# max file upload size - default is 20 meg
|
||||
UPLOAD_LIMIT=20000000
|
||||
|
||||
# Store uploads in S3-compatible service:
|
||||
# UPLOADS_S3_BUCKET=
|
||||
# UPLOADS_S3_ACCESS_KEY_ID=
|
||||
# UPLOADS_S3_SECRET_ACCESS_KEY=
|
||||
# UPLOADS_S3_REGION=fr-par
|
||||
# UPLOADS_S3_HOST=s3.fr-par.scw.cloud
|
||||
# UPLOADS_S3_SCHEME=https://
|
||||
# UPLOADS_S3_URL=
|
||||
# UPLOADS_S3_DEFAULT_URL=
|
||||
# AWS_ROLE_ARN=
|
||||
# AWS_WEB_IDENTITY_TOKEN_FILE=
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.s3.yml"
|
||||
#SECRET_UPLOADS_S3_ACCESS_KEY_ID_VERSION=v1
|
||||
#SECRET_UPLOADS_S3_SECRET_ACCESS_KEY_VERSION=v1
|
||||
#UPLOADS_S3_BUCKET=
|
||||
#UPLOADS_S3_REGION=fr-par
|
||||
#UPLOADS_S3_HOST=s3.fr-par.scw.cloud
|
||||
#UPLOADS_S3_SCHEME=https://
|
||||
#UPLOADS_S3_URL=
|
||||
#UPLOADS_S3_DEFAULT_URL=
|
||||
#AWS_ROLE_ARN=
|
||||
# Optionally use AWS identity token file
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.s3.tokenfile.yml"
|
||||
#SECRET_AWS_WEB_IDENTITY_TOKEN_VERSION=v1
|
||||
|
||||
# ====================================
|
||||
# SSO
|
||||
|
||||
# Enable using Bonfire as an SSO provider for external apps to sign in with?
|
||||
# ENABLE_SSO_PROVIDER=false
|
||||
OAUTH_ISSUER=https://${DOMAIN}
|
||||
#OAUTH_ISSUER=https://${DOMAIN}
|
||||
|
||||
# OpenID Connect: connect as a client to the OpenID Connect provider with callback url https://yourinstance.tld/oauth/client/openid_1
|
||||
# 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_DISCOVERY=
|
||||
# OPENID_1_DISPLAY_NAME=
|
||||
# OPENID_1_CLIENT_ID=
|
||||
# OPENID_1_CLIENT_SECRET=
|
||||
# OPENID_1_SCOPE=
|
||||
# OPENID_1_RESPONSE_TYPE=code
|
||||
# OPENID_1_ENABLE_SIGNUP=false
|
||||
# ^ can be code, token or id_token
|
||||
|
||||
# orcid.org SSO: connect as a client to the orcid.org OpenID Connect provider with callback url https://yourinstance.tld/oauth/client/orcid
|
||||
# orcid.org SSO: connect as a client to the orcid.org OpenID Connect provider with callback url https://yourinstance.tld/openid/client/orcid
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.auth.orcid.yml"
|
||||
#SECRET_ORCID_CLIENT_SECRET_VERSION=v1
|
||||
# ORCID_CLIENT_ID=
|
||||
# ORCID_CLIENT_SECRET=
|
||||
|
||||
# OAuth2 provider: connect as a client to the OAuth2 provider with callback url https://yourinstance.tld/oauth/client/oauth_1
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.auth.oauth.yml"
|
||||
# SECRET_OAUTH_CLIENT_SECRET_VERSION=v1
|
||||
# OAUTH_1_DISPLAY_NAME=
|
||||
# OAUTH_1_CLIENT_ID=
|
||||
# OAUTH_1_CLIENT_SECRET=
|
||||
# OAUTH_1_AUTHORIZE_URI=
|
||||
# OAUTH_1_ACCESS_TOKEN_URI=
|
||||
# OAUTH_1_USER_INFO_URI=
|
||||
# OAUTH_1_ENABLE_SIGNUP=false
|
||||
|
||||
# github.com SSO: connect as a client to the github.com OAuth2 provider with callback url https://yourinstance.tld/oauth/client/github
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.auth.github.yml"
|
||||
# SECRET_GITHUB_CLIENT_SECRET_VERSION=v1
|
||||
# GITHUB_APP_CLIENT_ID=
|
||||
# GITHUB_CLIENT_SECRET=
|
||||
|
||||
# More Bonfire extensions configs:
|
||||
# Zenodo SSO: connect as a client to the Zenodo OAuth2 provider with callback url https://yourinstance.tld/oauth/client/zenodo
|
||||
# ZENODO_CLIENT_ID=
|
||||
# ZENODO_CLIENT_SECRET=
|
||||
# ZENODO_GRANT_TYPE=
|
||||
# ZENODO_SCOPE=
|
||||
# ZENODO_ENV=
|
||||
|
||||
# ====================================
|
||||
# GHOST INTEGRATION
|
||||
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.ghost.yml"
|
||||
#SECRET_GHOST_CONTENT_API_KEY_VERSION=v1
|
||||
#SECRET_GHOST_ADMIN_API_KEY_VERSION=v1
|
||||
#SECRET_GHOST_WEBHOOK_SECRET_VERSION=v1
|
||||
#GHOST_URL=https://example.ghost.io
|
||||
|
||||
# ====================================
|
||||
# OPEN TELEMETRY
|
||||
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.otel.yml"
|
||||
#SECRET_HONEYCOMB_API_KEY_VERSION=v1
|
||||
#SECRET_LIGHTSTEP_API_KEY_VERSION=v1
|
||||
|
||||
# ====================================
|
||||
# BONFIRE EXTENSIONS AND MISC
|
||||
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.webpush.yml"
|
||||
# SECRET_WEBPUSH_PRIVATE_KEY_VERSION=v1
|
||||
# WEB_PUSH_SUBJECT=mailto:admin@example.com
|
||||
# WEB_PUSH_PUBLIC_KEY=xyz
|
||||
# WEB_PUSH_PRIVATE_KEY=abc
|
||||
# GEOLOCATE_OPENCAGEDATA=
|
||||
# GITHUB_TOKEN=xyz
|
||||
# AKISMET_API_KEY=
|
||||
|
||||
WITH_LV_NATIVE=0
|
||||
WITH_IMAGE_VIX=1
|
||||
WITH_AI=0
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.github.yml"
|
||||
#SECRET_GITHUB_TOKEN_VERSION=v1
|
||||
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.akismet.yml"
|
||||
#SECRET_AKISMET_API_KEY_VERSION=v1
|
||||
|
||||
#COMPOSE_FILE="$COMPOSE_FILE:compose.mapbox.yml"
|
||||
#SECRET_MAPBOX_API_KEY_VERSION=v1
|
||||
|
||||
# GEOLOCATE_OPENCAGEDATA=
|
||||
#IFRAME_ALLOWED_ORIGINS=https://example.com
|
||||
|
||||
# how much info to include in app logs (from less to more: emergency, alert, critical, error, warning, notice, info, debug); default is warning — note that debug-level entries are removed from release builds entirely, so the most verbose usable level here is info
|
||||
# PROD_LOG_LEVEL=warning
|
||||
|
||||
# how much info to include in logs (from less to more: emergency, alert, critical, error, warning, notice, info, debug)
|
||||
LOG_LEVEL=info
|
||||
|
||||
# error reporting:
|
||||
# SENTRY_DSN=
|
||||
|
||||
# ====================================
|
||||
# these secrets will be autogenerated/managed by abra and docker"
|
||||
SECRET_POSTGRES_PASSWORD_VERSION=v1
|
||||
SECRET_MEILI_MASTER_KEY_VERSION=v1
|
||||
# SECRETS
|
||||
# these secrets will be autogenerated/managed by abra and docker
|
||||
|
||||
SECRET_SEEDS_PW_VERSION=v1
|
||||
SECRET_LIVEBOOK_PASSWORD_VERSION=v1
|
||||
|
||||
SECRET_SECRET_KEY_BASE_VERSION=v1 # length=128
|
||||
SECRET_SIGNING_SALT_VERSION=v1 # length=128
|
||||
SECRET_ENCRYPTION_SALT_VERSION=v1 # length=128
|
||||
SECRET_RELEASE_COOKIE_VERSION=v1
|
||||
|
||||
# ====================================
|
||||
# You should not have to edit any of the following ones:
|
||||
APP_NAME=Bonfire
|
||||
LANG=en_US.UTF-8
|
||||
SEEDS_USER=root
|
||||
ERLANG_COOKIE=bonfire_cookie
|
||||
REPLACE_OS_VARS=true
|
||||
LIVEVIEW_ENABLED=true
|
||||
ACME_AGREE=true
|
||||
SHOW_DEBUG_IN_DEV=true
|
||||
LETS_ENCRYPT_ENV=production
|
||||
HOSTNAME=$DOMAIN
|
||||
#PLUG_SERVER=bandit
|
||||
|
||||
@@ -15,18 +15,38 @@ 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
|
||||
|
||||
[`abra`]: https://docs.coopcloud.tech/abra/
|
||||
[`coop-cloud/traefik`]: https://git.coopcloud.tech/coop-cloud/traefik
|
||||
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 <app-name>`
|
||||
|
||||
## 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
|
||||
|
||||
@@ -38,3 +58,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).
|
||||
@@ -1,4 +1,5 @@
|
||||
export APP_ENTRYPOINT_VERSION=v2
|
||||
export PG_BACKUP_VERSION=v4
|
||||
export APP_ENTRYPOINT_VERSION=v4
|
||||
export PG_BACKUP_VERSION=v6
|
||||
export MEILI_BACKUP_VERSION=v4
|
||||
|
||||
export SONIC_CFG_VERSION=v1
|
||||
export SONIC_ENTRYPOINT_VERSION=v1
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- AKISMET_API_KEY_FILE=/run/secrets/akismet_api_key
|
||||
|
||||
secrets:
|
||||
- akismet_api_key
|
||||
|
||||
secrets:
|
||||
akismet_api_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_akismet_api_key_${SECRET_AKISMET_API_KEY_VERSION:-v1}
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- GITHUB_APP_CLIENT_ID
|
||||
- GITHUB_CLIENT_SECRET_FILE=/run/secrets/github_client_secret
|
||||
secrets:
|
||||
- github_client_secret
|
||||
|
||||
secrets:
|
||||
oauth_client_secret:
|
||||
external: true
|
||||
name: ${STACK_NAME}_github_client_secret_${SECRET_GITHUB_CLIENT_SECRET_VERSION:-v1}
|
||||
@@ -0,0 +1,19 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- OAUTH_1_DISPLAY_NAME
|
||||
- OAUTH_1_CLIENT_ID
|
||||
- OAUTH_1_CLIENT_SECRET_FILE=/run/secrets/oauth_client_secret
|
||||
- OAUTH_1_AUTHORIZE_URI
|
||||
- OAUTH_1_ACCESS_TOKEN_URI
|
||||
- OAUTH_1_USER_INFO_URI
|
||||
- OAUTH_1_ENABLE_SIGNUP
|
||||
secrets:
|
||||
- oauth_client_secret
|
||||
|
||||
secrets:
|
||||
oauth_client_secret:
|
||||
external: true
|
||||
name: ${STACK_NAME}_oauth_client_secret_${SECRET_OAUTH_CLIENT_SECRET_VERSION:-v1}
|
||||
@@ -0,0 +1,19 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- OPENID_1_DISPLAY_NAME
|
||||
- OPENID_1_DISCOVERY
|
||||
- OPENID_1_CLIENT_ID
|
||||
- OPENID_1_CLIENT_SECRET_FILE=/run/secrets/openid_client_secret
|
||||
- OPENID_1_SCOPE
|
||||
- OPENID_1_RESPONSE_TYPE
|
||||
- OPENID_1_ENABLE_SIGNUP
|
||||
secrets:
|
||||
- openid_client_secret
|
||||
|
||||
secrets:
|
||||
openid_client_secret:
|
||||
external: true
|
||||
name: ${STACK_NAME}_openid_client_secret_${SECRET_OPENID_CLIENT_SECRET_VERSION:-v1}
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- ORCID_CLIENT_ID
|
||||
- ORCID_CLIENT_SECRET_FILE=/run/secrets/orchid_client_secret
|
||||
secrets:
|
||||
- orchid_client_secret
|
||||
|
||||
secrets:
|
||||
orchid_client_secret:
|
||||
external: true
|
||||
name: ${STACK_NAME}_orchid_client_secret_${SECRET_ORCID_CLIENT_SECRET_VERSION:-v1}
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
app:
|
||||
secrets:
|
||||
- usersfile
|
||||
deploy:
|
||||
labels:
|
||||
- "traefik.http.middlewares.${STACK_NAME}_basicauth.basicauth.usersFile=/run/secrets/usersfile"
|
||||
- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}_basicauth"
|
||||
|
||||
secrets:
|
||||
usersfile:
|
||||
name: ${STACK_NAME}_usersfile_${SECRET_USERSFILE_VERSION}
|
||||
external: true
|
||||
@@ -0,0 +1,24 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- GHOST_URL
|
||||
- GHOST_CONTENT_API_KEY_FILE=/run/secrets/ghost_content_api_key
|
||||
- GHOST_ADMIN_API_KEY_FILE=/run/secrets/ghost_admin_api_key
|
||||
- GHOST_WEBHOOK_SECRET_FILE=/run/secrets/ghost_webhook_secret
|
||||
secrets:
|
||||
- ghost_content_api_key
|
||||
- ghost_admin_api_key
|
||||
- ghost_webhook_secret
|
||||
|
||||
secrets:
|
||||
ghost_content_api_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_ghost_content_api_key_${SECRET_GHOST_CONTENT_API_KEY_VERSION:-v1}
|
||||
ghost_admin_api_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_ghost_admin_api_key_${SECRET_GHOST_ADMIN_API_KEY_VERSION:-v1}
|
||||
ghost_webhook_secret:
|
||||
external: true
|
||||
name: ${STACK_NAME}_ghost_webhook_secret_${SECRET_GHOST_WEBHOOK_SECRET_VERSION:-v1}
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- GITHUB_TOKEN_FILE=/run/secrets/github_token
|
||||
|
||||
secrets:
|
||||
- github_token
|
||||
|
||||
secrets:
|
||||
github_token:
|
||||
external: true
|
||||
name: ${STACK_NAME}_github_token_${SECRET_GITHUB_TOKEN_VERSION:-v1}
|
||||
@@ -0,0 +1,13 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- MAIL_PRIVATE_KEY_FILE=/run/secrets/mail_private_key
|
||||
secrets:
|
||||
- mail_private_key
|
||||
|
||||
secrets:
|
||||
mail_private_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_mail_private_key_${SECRET_MAIL_PRIVATE_KEY_VERSION:-v1}
|
||||
+19
-3
@@ -2,10 +2,26 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- MAIL_BACKEND=${MAIL_BACKEND:-none}
|
||||
- MAIL_DOMAIN
|
||||
- MAIL_FROM
|
||||
- MAIL_KEY_FILE=/run/secrets/mail_key
|
||||
- MAIL_PROJECT_ID
|
||||
- MAIL_BASE_URI
|
||||
- MAIL_REGION
|
||||
- MAIL_SERVER
|
||||
- MAIL_USER
|
||||
- MAIL_PORT
|
||||
- MAIL_TLS
|
||||
- MAIL_SSL
|
||||
- MAIL_SMTP_AUTH
|
||||
- MAIL_RETRIES
|
||||
- MAIL_ARGS
|
||||
secrets:
|
||||
- mail_password
|
||||
- mail_key
|
||||
|
||||
secrets:
|
||||
mail_password:
|
||||
mail_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_mail_password_${SECRET_MAIL_PASSWORD_VERSION}
|
||||
name: ${STACK_NAME}_mail_key_${SECRET_MAIL_KEY_VERSION:-v1}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- MAPBOX_API_KEY_FILE=/run/secrets/mapbox_api_key
|
||||
|
||||
secrets:
|
||||
- mapbox_api_key
|
||||
|
||||
secrets:
|
||||
mapbox_api_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_mapbox_api_key_${SECRET_MAPBOX_API_KEY_VERSION:-v1}
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
- search
|
||||
environment:
|
||||
- SEARCH_MEILI_INSTANCE=http://${STACK_NAME}_search:7700
|
||||
|
||||
search:
|
||||
image: getmeili/meilisearch:v1.14 # WIP: upgrade from v1.11 to 1.14
|
||||
secrets:
|
||||
- meili_master_key
|
||||
volumes:
|
||||
- "search-data:/meili_data"
|
||||
- "dump-data:/meili_dumps"
|
||||
networks:
|
||||
- internal
|
||||
entrypoint: ["tini", "--", "/docker-entrypoint.sh", "/bin/meilisearch"]
|
||||
environment:
|
||||
- MEILI_DUMP_DIR=/meili_dumps
|
||||
configs:
|
||||
- source: app_entrypoint
|
||||
target: /docker-entrypoint.sh
|
||||
mode: 0555
|
||||
- source: meili_backup
|
||||
target: /meili_backup.sh
|
||||
mode: 0555
|
||||
labels:
|
||||
backupbot.backup: ${ENABLE_BACKUPS:-true}
|
||||
backupbot.backup.volumes.search-data: "false"
|
||||
backupbot.backup.volumes.dump-data.path: "/meili_dumps/meilisearch_latest.dump"
|
||||
backupbot.backup.pre-hook: "/meili_backup.sh backup"
|
||||
backupbot.restore.post-hook: '/meili_backup.sh restore'
|
||||
|
||||
volumes:
|
||||
search-data:
|
||||
dump-data:
|
||||
|
||||
configs:
|
||||
meili_backup:
|
||||
name: ${STACK_NAME}_meili_backup_${MEILI_BACKUP_VERSION}
|
||||
file: meili_backup.sh
|
||||
@@ -0,0 +1,21 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- OTEL_ENABLED
|
||||
- OTEL_SERVICE_NAME
|
||||
- OTEL_HONEYCOMB_API_KEY_FILE=/run/secrets/honeycomb_api_key
|
||||
- OTEL_LIGHTSTEP_API_KEY_FILE=/run/secrets/lightstep_api_key
|
||||
|
||||
secrets:
|
||||
- honeycomb_api_key
|
||||
- lightstep_api_key
|
||||
|
||||
secrets:
|
||||
honeycomb_api_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_honeycomb_api_key_${SECRET_HONEYCOMB_API_KEY_VERSION:-v1}
|
||||
lightstep_api_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_lightstep_api_key_${SECRET_LIGHTSTEP_API_KEY_VERSION:-v1}
|
||||
@@ -0,0 +1,93 @@
|
||||
version: '3.8'
|
||||
|
||||
x-postgres-env: &postgres-env
|
||||
POSTGRES_DB: bonfire_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
<<: *postgres-env
|
||||
POSTGRES_HOST: ${STACK_NAME}_db
|
||||
secrets:
|
||||
- postgres_password
|
||||
|
||||
db:
|
||||
image: ${DB_DOCKER_IMAGE:-ghcr.io/baosystems/postgis}:${DB_DOCKER_VERSION:-17-3.5}
|
||||
# give Postgres time to shut down cleanly: the Swarm default (10s) force-kills it mid-shutdown on every redeploy, which wipes the cumulative stats (pg_stat_*) that autovacuum's triggers depend on — a root cause of autovacuum never running on big tables
|
||||
stop_grace_period: 2m # NOTE: abra validates the schema before env interpolation, so this duration field can't be env-parameterized
|
||||
# static tuning defaults for deployments NOT using the postgres tuner overlay (NOTE: `compose.postgres.tune.yml` computes these from your system resources instead of hardcoding them, so please use it!)
|
||||
# Notes:
|
||||
# memory defaults are deliberately SAFE-SMALL (fit a 1 or 2GB VPS with the app co-hosted): shared_buffers is allocated at boot and maintenance_work_mem is per vacuum worker, so oversizing can prevent startup or OOM small machines — the tuner overlay right-sizes them instead (~25% / ~6% of the DB's RAM budget);
|
||||
# max_connections is deliberately low because Bonfire's Ecto pool is the connection pooler (~25-50 conns; oversizing shrinks usable work_mem);
|
||||
# jit off = measured per-query compile tax on Bonfire's many-join queries with no benefit;
|
||||
# the autovacuum settings suit Bonfire's soft-delete/append workload where default thresholds never trigger on big tables.
|
||||
command: >
|
||||
postgres
|
||||
-c max_connections=${PG_MAX_CONNECTIONS:-100}
|
||||
-c shared_buffers=${PG_SHARED_BUFFERS_MB:-256}MB
|
||||
-c effective_cache_size=${PG_EFFECTIVE_CACHE_SIZE_MB:-768}MB
|
||||
-c work_mem=${PG_WORK_MEM_MB:-16}MB
|
||||
-c maintenance_work_mem=${PG_MAINTENANCE_WORK_MEM_MB:-128}MB
|
||||
-c random_page_cost=${PG_RANDOM_PAGE_COST:-1.1}
|
||||
-c effective_io_concurrency=${PG_EFFECTIVE_IO_CONCURRENCY:-200}
|
||||
-c jit=${PG_JIT:-off}
|
||||
-c wal_compression=${PG_WAL_COMPRESSION:-lz4}
|
||||
-c default_toast_compression=${PG_TOAST_COMPRESSION:-lz4}
|
||||
-c shared_preload_libraries=${PG_PRELOAD_LIBS:-pg_stat_statements}
|
||||
-c pg_stat_statements.track=all
|
||||
-c track_io_timing=on
|
||||
-c track_activity_query_size=16384
|
||||
-c autovacuum_vacuum_scale_factor=0.05
|
||||
-c autovacuum_vacuum_insert_scale_factor=0.05
|
||||
-c autovacuum_vacuum_cost_limit=1000
|
||||
-c log_autovacuum_min_duration=0
|
||||
-c log_min_duration_statement=${PG_LOG_MIN_DURATION_STATEMENT:-2000}
|
||||
-c log_lock_waits=on
|
||||
-c log_temp_files=0
|
||||
# -c statement_timeout=1800000
|
||||
#entrypoint: ['tail', '-f', '/dev/null'] # uncomment when the Postgres DB is corrupted and won't start
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
- type: tmpfs
|
||||
target: /dev/shm
|
||||
tmpfs:
|
||||
size: 1000000000
|
||||
# about 1 GB in bytes
|
||||
tmpfs:
|
||||
- /tmp:size=${DB_MEMORY_LIMIT:-1024}M^
|
||||
networks:
|
||||
- internal
|
||||
environment:
|
||||
<<: *postgres-env
|
||||
secrets:
|
||||
- postgres_password
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -h localhost -U $$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
labels:
|
||||
backupbot.backup: ${ENABLE_BACKUPS:-true}
|
||||
backupbot.backup.pre-hook: "/pg_backup.sh backup"
|
||||
backupbot.backup.volumes.db-data.path: "backup.sql"
|
||||
backupbot.restore.post-hook: '/pg_backup.sh restore'
|
||||
configs:
|
||||
- source: pg_backup
|
||||
target: /pg_backup.sh
|
||||
mode: 0555
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
|
||||
configs:
|
||||
pg_backup:
|
||||
name: ${STACK_NAME}_pg_backup_${PG_BACKUP_VERSION}
|
||||
file: pg_backup.sh
|
||||
|
||||
secrets:
|
||||
postgres_password:
|
||||
external: true
|
||||
name: ${STACK_NAME}_postgres_password_${SECRET_POSTGRES_PASSWORD_VERSION:-v1}
|
||||
@@ -0,0 +1,14 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- AWS_WEB_IDENTITY_TOKEN_FILE=/run/secrets/aws_web_identity_token
|
||||
|
||||
secrets:
|
||||
- aws_web_identity_token
|
||||
|
||||
secrets:
|
||||
aws_web_identity_token:
|
||||
external: true
|
||||
name: ${STACK_NAME}_aws_web_identity_token_${SECRET_AWS_WEB_IDENTITY_TOKEN_VERSION:-v1}
|
||||
@@ -0,0 +1,31 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- UPLOADS_S3_BUCKET
|
||||
- UPLOADS_S3_ACCESS_KEY_ID_FILE=/run/secrets/uploads_s3_access_key_id
|
||||
- UPLOADS_S3_SECRET_ACCESS_KEY_FILE=/run/secrets/uploads_s3_secret_access_key
|
||||
- UPLOADS_S3_REGION
|
||||
- UPLOADS_S3_HOST
|
||||
- UPLOADS_S3_SCHEME
|
||||
- UPLOADS_S3_URL
|
||||
- UPLOADS_S3_DEFAULT_URL
|
||||
- UPLOADS_S3_URL_EXPIRATION_TTL
|
||||
- AWS_ROLE_ARN
|
||||
|
||||
secrets:
|
||||
- mail_key
|
||||
- uploads_s3_access_key_id
|
||||
- uploads_s3_secret_access_key
|
||||
|
||||
secrets:
|
||||
mail_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_mail_key_${SECRET_MAIL_KEY_VERSION:-v1}
|
||||
uploads_s3_access_key_id:
|
||||
external: true
|
||||
name: ${STACK_NAME}_uploads_s3_access_key_id_${SECRET_UPLOADS_S3_ACCESS_KEY_ID_VERSION:-v1}
|
||||
uploads_s3_secret_access_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_uploads_s3_secret_access_key_${SECRET_UPLOADS_S3_SECRET_ACCESS_KEY_VERSION:-v1}
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
- search
|
||||
environment:
|
||||
- SEARCH_ADAPTER=sonic
|
||||
- SONIC_HOST=${STACK_NAME}_search
|
||||
- SONIC_PORT=1491
|
||||
- SONIC_PASSWORD
|
||||
# - SONIC_PASSWORD_FILE=/run/secrets/sonic_password
|
||||
# secrets:
|
||||
# - sonic_password
|
||||
|
||||
search:
|
||||
image: valeriansaliou/sonic:v1.7.4
|
||||
# secrets:
|
||||
# - sonic_password
|
||||
volumes:
|
||||
- "sonic-data:/var/lib/sonic/store"
|
||||
networks:
|
||||
- internal
|
||||
# NOTE: latest versions of Sonic (v1.5.1+) don't have a shell, so we can't have a custom entrypoint script that does pre-startup configuration, so we just need to store the PW in env for now
|
||||
# entrypoint: ["/docker-entrypoint.sh"]
|
||||
configs:
|
||||
- source: sonic_cfg
|
||||
target: /etc/sonic.cfg
|
||||
mode: 0444
|
||||
# - source: sonic_entrypoint
|
||||
# target: /docker-entrypoint.sh
|
||||
# mode: 0555
|
||||
|
||||
volumes:
|
||||
sonic-data:
|
||||
|
||||
configs:
|
||||
sonic_cfg:
|
||||
name: ${STACK_NAME}_sonic_cfg_${SONIC_CFG_VERSION:-v1}
|
||||
file: sonic.cfg
|
||||
# sonic_entrypoint:
|
||||
# name: ${STACK_NAME}_sonic_entrypoint_${SONIC_ENTRYPOINT_VERSION:-v1}
|
||||
# file: sonic_entrypoint.sh
|
||||
|
||||
# secrets:
|
||||
# sonic_password:
|
||||
# external: true
|
||||
# name: ${STACK_NAME}_sonic_password_${SECRET_SONIC_PASSWORD_VERSION:-v1}
|
||||
@@ -0,0 +1,15 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
- WEB_PUSH_SUBJECT
|
||||
- WEB_PUSH_PUBLIC_KEY
|
||||
- WEB_PUSH_PRIVATE_KEY_FILE=/run/secrets/webpush_private_key
|
||||
secrets:
|
||||
- webpush_private_key
|
||||
|
||||
secrets:
|
||||
webpush_private_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_webpush_private_key_${SECRET_WEBPUSH_PRIVATE_KEY_VERSION:-v1}
|
||||
+45
-152
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: ${APP_DOCKER_IMAGE}
|
||||
image: ${CONTAINER_REGISTRY:-bonfirenetworks/bonfire}:${APP_VERSION:-1.0.6}-${APP_FLAVOUR:-social}-${APP_PLATFORM:-amd64}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
@@ -12,114 +12,59 @@ services:
|
||||
depends_on:
|
||||
- db
|
||||
environment:
|
||||
- POSTGRES_HOST=${STACK_NAME}_db
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=bonfire_db
|
||||
- PUBLIC_PORT=443
|
||||
- MIX_ENV=prod
|
||||
|
||||
- HOSTNAME
|
||||
- HOSTNAME=${DOMAIN}
|
||||
- INSTANCE_DESCRIPTION
|
||||
- DISABLE_DB_AUTOMIGRATION
|
||||
- UPLOAD_LIMIT
|
||||
- INVITE_KEY
|
||||
- LANG
|
||||
- SEEDS_USER
|
||||
- ERLANG_COOKIE
|
||||
- REPLACE_OS_VARS
|
||||
- LIVEVIEW_ENABLED
|
||||
- APP_NAME
|
||||
- LANG=${LANG:-en_US.UTF-8}
|
||||
- SEEDS_USER=${SEEDS_USER:-root}
|
||||
- REPLACE_OS_VARS=${REPLACE_OS_VARS:-true}
|
||||
- LIVEVIEW_ENABLED=${LIVEVIEW_ENABLED:-true}
|
||||
- APP_NAME=${APP_NAME:-Bonfire}
|
||||
- PLUG_SERVER
|
||||
- WITH_LV_NATIVE
|
||||
- WITH_IMAGE_VIX
|
||||
- WITH_AI
|
||||
- WITH_LV_NATIVE=${WITH_LV_NATIVE:-0}
|
||||
- WITH_IMAGE_VIX=${WITH_IMAGE_VIX:-1}
|
||||
- WITH_AI=${WITH_AI:-0}
|
||||
- LIVE_DASHBOARD_LOGGER=${LIVE_DASHBOARD_LOGGER:-false}
|
||||
- IFRAME_ALLOWED_ORIGINS
|
||||
- RELEASE_DISTRIBUTION
|
||||
- RELEASE_NODE
|
||||
- RELEASE_MODE
|
||||
|
||||
- DB_SLOW_QUERY_MS
|
||||
- DB_STATEMENT_TIMEOUT
|
||||
- DB_QUERY_TIMEOUT
|
||||
- DB_JIT
|
||||
- DB_MIGRATE_INDEXES_CONCURRENTLY
|
||||
|
||||
- MAIL_BACKEND
|
||||
- MAIL_DOMAIN
|
||||
- MAIL_FROM
|
||||
- MAIL_KEY
|
||||
- MAIL_PROJECT_ID
|
||||
- MAIL_PRIVATE_KEY
|
||||
- MAIL_BASE_URI
|
||||
- MAIL_REGION
|
||||
- MAIL_SESSION_TOKEN
|
||||
- MAIL_SERVER
|
||||
- MAIL_USER
|
||||
- MAIL_PASSWORD
|
||||
- MAIL_PORT
|
||||
- MAIL_TLS
|
||||
- MAIL_SSL
|
||||
- MAIL_SMTP_AUTH
|
||||
- MAIL_RETRIES
|
||||
- MAIL_ARGS
|
||||
- PROD_LOG_LEVEL
|
||||
|
||||
- SENTRY_DSN
|
||||
- OTEL_ENABLED
|
||||
- OTEL_SERVICE_NAME
|
||||
- OTEL_HONEYCOMB_API_KEY
|
||||
- OTEL_LIGHTSTEP_API_KEY
|
||||
|
||||
- WEB_PUSH_SUBJECT
|
||||
- WEB_PUSH_PUBLIC_KEY
|
||||
- WEB_PUSH_PRIVATE_KEY
|
||||
|
||||
- AKISMET_API_KEY
|
||||
- MAPBOX_API_KEY
|
||||
|
||||
- GEOLOCATE_OPENCAGEDATA
|
||||
- GITHUB_TOKEN
|
||||
|
||||
- UPLOADS_S3_BUCKET
|
||||
- UPLOADS_S3_ACCESS_KEY_ID
|
||||
- UPLOADS_S3_SECRET_ACCESS_KEY
|
||||
- UPLOADS_S3_REGION
|
||||
- UPLOADS_S3_HOST
|
||||
- UPLOADS_S3_SCHEME
|
||||
- UPLOADS_S3_URL
|
||||
- UPLOADS_S3_DEFAULT_URL
|
||||
- UPLOADS_S3_URL_EXPIRATION_TTL
|
||||
- AWS_ROLE_ARN
|
||||
- AWS_WEB_IDENTITY_TOKEN_FILE
|
||||
|
||||
- ENABLE_SSO_PROVIDER
|
||||
- OAUTH_ISSUER
|
||||
|
||||
- OPENID_1_DISPLAY_NAME
|
||||
- OPENID_1_DISCOVERY
|
||||
- OPENID_1_CLIENT_ID
|
||||
- OPENID_1_CLIENT_SECRET
|
||||
- OPENID_1_SCOPE
|
||||
- OPENID_1_RESPONSE_TYPE
|
||||
- OPENID_1_ENABLE_SIGNUP
|
||||
|
||||
- OAUTH_1_DISPLAY_NAME
|
||||
- OAUTH_1_CLIENT_ID
|
||||
- OAUTH_1_CLIENT_SECRET
|
||||
- OAUTH_1_AUTHORIZE_URI
|
||||
- OAUTH_1_ACCESS_TOKEN_URI
|
||||
- OAUTH_1_USER_INFO_URI
|
||||
- OAUTH_1_ENABLE_SIGNUP
|
||||
|
||||
- GITHUB_APP_CLIENT_ID
|
||||
- GITHUB_CLIENT_SECRET
|
||||
- SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base
|
||||
- SIGNING_SALT_FILE=/run/secrets/signing_salt
|
||||
- ENCRYPTION_SALT_FILE=/run/secrets/encryption_salt
|
||||
- SEEDS_PW_FILE=/run/secrets/seeds_pw
|
||||
- LIVEBOOK_PASSWORD_FILE=/run/secrets/livebook_password
|
||||
- RELEASE_COOKIE_FILE=/run/secrets/release_cookie
|
||||
|
||||
- ORCID_CLIENT_ID
|
||||
- ORCID_CLIENT_SECRET
|
||||
|
||||
secrets:
|
||||
- postgres_password
|
||||
- secret_key_base
|
||||
- signing_salt
|
||||
- encryption_salt
|
||||
- meili_master_key
|
||||
- seeds_pw
|
||||
- livebook_password
|
||||
- release_cookie
|
||||
volumes:
|
||||
- upload-data:/opt/app/data/uploads
|
||||
# - backup-data:/opt/app/data/backup
|
||||
networks:
|
||||
- proxy
|
||||
- internal
|
||||
@@ -132,71 +77,25 @@ services:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
labels:
|
||||
# how long abra waits for the deploy to converge, as needs headroom for DB migrations on upgrades
|
||||
# and the db service's 2m stop_grace_period during redeploys
|
||||
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT:-300}"
|
||||
- "backupbot.backup=${ENABLE_BACKUPS:-true}"
|
||||
#- backupbot.backup.volumes.upload-data: "true"
|
||||
#- backupbot.backup.volumes.upload-data.path: "/opt/app/data/uploads"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=4000"
|
||||
- "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}"
|
||||
#- "traefik.http.routers.${STACK_NAME}.middlewares=error-pages-middleware"
|
||||
#- "traefik.http.services.${STACK_NAME}.loadbalancer.server.port=80"
|
||||
#- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect"
|
||||
#- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true"
|
||||
#- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}"
|
||||
# healthcheck:
|
||||
# test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
# interval: 30s
|
||||
# timeout: 10s
|
||||
# retries: 10
|
||||
# start_period: 1m
|
||||
|
||||
db:
|
||||
image: ${DB_DOCKER_IMAGE}
|
||||
environment:
|
||||
# - POSTGRES_PASSWORD
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_DB=bonfire_db
|
||||
- POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password
|
||||
secrets:
|
||||
- postgres_password
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
- type: tmpfs
|
||||
target: /dev/shm
|
||||
tmpfs:
|
||||
size: 1000000000
|
||||
# about 1 GB in bytes ^
|
||||
networks:
|
||||
- internal
|
||||
# shm_size: ${DB_MEMORY_LIMIT} # unsupported by docker swarm
|
||||
tmpfs:
|
||||
- /tmp:size=${DB_MEMORY_LIMIT}M
|
||||
command: >
|
||||
postgres
|
||||
-c max_connections=200
|
||||
-c shared_buffers=${DB_MEMORY_LIMIT}MB
|
||||
# -c shared_preload_libraries='pg_stat_statements'
|
||||
# -c statement_timeout=1800000
|
||||
# -c pg_stat_statements.track=all
|
||||
#entrypoint: ['tail', '-f', '/dev/null'] # uncomment when the Postgres DB is corrupted and won't start
|
||||
deploy:
|
||||
labels:
|
||||
backupbot.backup: ${ENABLE_BACKUPS:-true}
|
||||
# backupbot.backup.volumes.db-data: false
|
||||
backupbot.backup.volumes.db-data.path: "backup.sql"
|
||||
backupbot.backup.pre-hook: "/pg_backup.sh backup"
|
||||
backupbot.restore.post-hook: '/pg_backup.sh restore'
|
||||
configs:
|
||||
- source: pg_backup
|
||||
target: /pg_backup.sh
|
||||
mode: 0555
|
||||
- "coop-cloud.${STACK_NAME}.version=2.0.1+1.0.6-social-amd64"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:4000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 15s
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
upload-data:
|
||||
# backup-data:
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
@@ -205,32 +104,26 @@ networks:
|
||||
|
||||
configs:
|
||||
app_entrypoint:
|
||||
name: ${STACK_NAME}_app_entrypoint_${APP_ENTRYPOINT_VERSION}
|
||||
name: ${STACK_NAME}_app_entrypoint_${APP_ENTRYPOINT_VERSION:-v3}
|
||||
file: entrypoint.sh.tmpl
|
||||
template_driver: golang
|
||||
pg_backup:
|
||||
name: ${STACK_NAME}_pg_backup_${PG_BACKUP_VERSION}
|
||||
file: pg_backup.sh
|
||||
|
||||
secrets:
|
||||
postgres_password:
|
||||
external: true
|
||||
name: ${STACK_NAME}_postgres_password_${SECRET_POSTGRES_PASSWORD_VERSION}
|
||||
secret_key_base:
|
||||
external: true
|
||||
name: ${STACK_NAME}_secret_key_base_${SECRET_SECRET_KEY_BASE_VERSION}
|
||||
name: ${STACK_NAME}_secret_key_base_${SECRET_SECRET_KEY_BASE_VERSION:-v1}
|
||||
signing_salt:
|
||||
external: true
|
||||
name: ${STACK_NAME}_signing_salt_${SECRET_SIGNING_SALT_VERSION}
|
||||
name: ${STACK_NAME}_signing_salt_${SECRET_SIGNING_SALT_VERSION:-v1}
|
||||
encryption_salt:
|
||||
external: true
|
||||
name: ${STACK_NAME}_encryption_salt_${SECRET_ENCRYPTION_SALT_VERSION}
|
||||
meili_master_key:
|
||||
external: true
|
||||
name: ${STACK_NAME}_meili_master_key_${SECRET_MEILI_MASTER_KEY_VERSION}
|
||||
name: ${STACK_NAME}_encryption_salt_${SECRET_ENCRYPTION_SALT_VERSION:-v1}
|
||||
seeds_pw:
|
||||
external: true
|
||||
name: ${STACK_NAME}_seeds_pw_${SECRET_SEEDS_PW_VERSION}
|
||||
name: ${STACK_NAME}_seeds_pw_${SECRET_SEEDS_PW_VERSION:-v1}
|
||||
livebook_password:
|
||||
external: true
|
||||
name: ${STACK_NAME}_livebook_password_${SECRET_LIVEBOOK_PASSWORD_VERSION}
|
||||
name: ${STACK_NAME}_livebook_password_${SECRET_LIVEBOOK_PASSWORD_VERSION:-v1}
|
||||
release_cookie:
|
||||
external: true
|
||||
name: ${STACK_NAME}_release_cookie_${SECRET_RELEASE_COOKIE_VERSION:-v1}
|
||||
+3
-12
@@ -1,17 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# put secrets from files into env
|
||||
export MEILI_MASTER_KEY=$(cat /run/secrets/meili_master_key)
|
||||
export POSTGRES_PASSWORD=$(cat /run/secrets/postgres_password)
|
||||
export SECRET_KEY_BASE=$(cat /run/secrets/secret_key_base)
|
||||
export SIGNING_SALT=$(cat /run/secrets/signing_salt)
|
||||
export ENCRYPTION_SALT=$(cat /run/secrets/encryption_salt)
|
||||
export SEEDS_PW=$(cat /run/secrets/seeds_pw)
|
||||
export LIVEBOOK_PASSWORD=$(cat /run/secrets/livebook_password)
|
||||
|
||||
# Only read the secret when the MAIL_PASSWORD was not set to remain backwards compatible
|
||||
if [[ -z ${MAIL_PASSWORD} ]]; then
|
||||
export MAIL_PASSWORD=$(cat /run/secrets/mail_password)
|
||||
# meilisearch does not support MEILI_MASTER_KEY_FILE, so we export it here for the search service
|
||||
if [ -f /run/secrets/meili_master_key ]; then
|
||||
export MEILI_MASTER_KEY=$(cat /run/secrets/meili_master_key)
|
||||
fi
|
||||
|
||||
echo "....Secrets have been loaded, now run $@...."
|
||||
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
backup() {
|
||||
SECRET=$(cat /run/secrets/meili_master_key)
|
||||
# Create dump
|
||||
echo "Creating new Meilisearch dump..."
|
||||
RESPONSE=$(curl -s -X POST 'http://localhost:7700/dumps' -H "Authorization: Bearer $SECRET")
|
||||
echo "Response: $RESPONSE"
|
||||
|
||||
# More robust extraction of task UID
|
||||
TASK_UID=$(echo "$RESPONSE" | sed -n 's/.*"taskUid":\([0-9]*\).*/\1/p')
|
||||
|
||||
if [ -z "$TASK_UID" ]; then
|
||||
echo "Failed to extract task UID from response. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting for dump creation (task $TASK_UID)..."
|
||||
|
||||
MAX_ATTEMPTS=600
|
||||
ATTEMPT=0
|
||||
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
||||
RESPONSE=$(curl -s "http://localhost:7700/tasks/$TASK_UID" -H "Authorization: Bearer $SECRET")
|
||||
echo "Task status response: $RESPONSE"
|
||||
|
||||
TASK_STATUS=$(echo "$RESPONSE" | sed -n 's/.*"status":"\([^"]*\)".*/\1/p')
|
||||
|
||||
if [ -z "$TASK_STATUS" ]; then
|
||||
echo "Failed to extract task status. Retrying..."
|
||||
ATTEMPT=$((ATTEMPT+1))
|
||||
sleep 5
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Current status: $TASK_STATUS"
|
||||
|
||||
if [ "$TASK_STATUS" = "succeeded" ]; then
|
||||
echo "Dump creation succeeded"
|
||||
break
|
||||
elif [ "$TASK_STATUS" = "enqueued" ] || [ "$TASK_STATUS" = "processing" ]; then
|
||||
echo "Dump creation in progress... ($TASK_STATUS)"
|
||||
ATTEMPT=$((ATTEMPT+1))
|
||||
sleep 5
|
||||
else
|
||||
echo "Dump creation in unexpected state: $TASK_STATUS. Giving up."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
|
||||
echo "Timed out waiting for dump creation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract dump UID more reliably
|
||||
DUMP_UID=$(echo "$RESPONSE" | sed -n 's/.*"dumpUid":"\([^"]*\)".*/\1/p')
|
||||
|
||||
if [ -z "$DUMP_UID" ]; then
|
||||
echo "Failed to extract dump UID. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using dump $DUMP_UID"
|
||||
|
||||
# Check if file exists before copying
|
||||
if [ ! -f "/meili_dumps/$DUMP_UID.dump" ]; then
|
||||
echo "Dump file /meili_dumps/$DUMP_UID.dump not found!"
|
||||
ls -la /meili_dumps/
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp -f "/meili_dumps/$DUMP_UID.dump" "/meili_dumps/meilisearch_latest.dump"
|
||||
echo "Dump created and copied successfully. You can find it at /meili_dumps/meilisearch_latest.dump"
|
||||
}
|
||||
|
||||
restore() {
|
||||
echo 'Restarting Meilisearch with imported dump, may take a while to become available...'
|
||||
|
||||
# Check if dump file exists
|
||||
if [ ! -f "/meili_dumps/meilisearch_latest.dump" ]; then
|
||||
echo "Error: Dump file not found at /meili_dumps/meilisearch_latest.dump"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
pkill meilisearch || echo "No Meilisearch process found to kill"
|
||||
|
||||
echo "Starting Meilisearch with import dump option..."
|
||||
MEILI_NO_ANALYTICS=true /bin/meilisearch --import-dump /meili_dumps/meilisearch_latest.dump &
|
||||
|
||||
echo "Meilisearch restore process initiated..."
|
||||
}
|
||||
|
||||
# Handle command line argument
|
||||
case "$1" in
|
||||
backup)
|
||||
backup
|
||||
;;
|
||||
restore)
|
||||
restore
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {backup|restore}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
+8
-9
@@ -6,18 +6,17 @@ BACKUP_PATH="/var/lib/postgresql/data"
|
||||
LATEST_BACKUP_FILE="${BACKUP_PATH}/backup.sql"
|
||||
|
||||
function backup {
|
||||
FILE_WITH_DATE="${BACKUP_PATH}/backup_$(date +%F).sql"
|
||||
|
||||
if [ -f "$POSTGRES_PASSWORD_FILE" ]; then
|
||||
export PGPASSWORD=$(cat "$POSTGRES_PASSWORD_FILE")
|
||||
fi
|
||||
|
||||
echo "Creating backup at ${FILE_WITH_DATE}..."
|
||||
pg_dump -U "${POSTGRES_USER:-postgres}" "${POSTGRES_DB:-postgres}" > "${FILE_WITH_DATE}"
|
||||
|
||||
echo "Copying to ${LATEST_BACKUP_FILE}..."
|
||||
cp -f "${FILE_WITH_DATE}" "${LATEST_BACKUP_FILE}"
|
||||
|
||||
|
||||
# Keep a single backup.sql (restic handles versioning); write to a temp file and move
|
||||
# atomically so a failed dump never clobbers the last good backup.
|
||||
echo "Creating backup at ${LATEST_BACKUP_FILE}..."
|
||||
rm -f "${LATEST_BACKUP_FILE}.tmp"
|
||||
pg_dump -U "${POSTGRES_USER:-postgres}" "${POSTGRES_DB:-postgres}" > "${LATEST_BACKUP_FILE}.tmp"
|
||||
mv -f "${LATEST_BACKUP_FILE}.tmp" "${LATEST_BACKUP_FILE}"
|
||||
|
||||
echo "Backup done. You will find it at ${LATEST_BACKUP_FILE}"
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ services:
|
||||
# # -c shared_preload_libraries='pg_stat_statements'
|
||||
# # -c statement_timeout=1800000
|
||||
# # -c pg_stat_statements.track=all
|
||||
# give Postgres time to shut down cleanly: the Swarm default (10s) force-kills it mid-shutdown on every redeploy, which wipes the cumulative stats (pg_stat_*) that autovacuum's triggers depend on — a root cause of autovacuum never running on big tables
|
||||
stop_grace_period: 2m # NOTE: abra validates the schema before env interpolation, so this duration field can't be env-parameterized
|
||||
# shm_size: ${DB_MEMORY_LIMIT} # unsupported by docker swarm
|
||||
tmpfs:
|
||||
- /tmp:size=${DB_MEMORY_LIMIT}M
|
||||
@@ -40,10 +42,10 @@ configs:
|
||||
name: ${STACK_NAME}_postgres_entrypoint_${POSTGRES_ENTRYPOINT_VERSION:-v6}
|
||||
file: postgres/postgres-entrypoint.sh
|
||||
postgres_tune:
|
||||
name: ${STACK_NAME}_postgres_tune_${POSTGRES_TUNE_VERSION:-v1}
|
||||
name: ${STACK_NAME}_postgres_tune_${POSTGRES_TUNE_VERSION:-v2}
|
||||
file: postgres/postgres-tune.sh
|
||||
postgres_conf_tmpl:
|
||||
name: ${STACK_NAME}_postgres_conf_tmpl_${POSTGRES_CONF_TMPL_VERSION:-v2}
|
||||
name: ${STACK_NAME}_postgres_conf_tmpl_${POSTGRES_CONF_TMPL_VERSION:-v3}
|
||||
file: postgres/postgres.conf.tmpl
|
||||
bash_envsubst:
|
||||
name: ${STACK_NAME}_bash_envsubst_${BASH_ENVSUBST_VERSION:-v1}
|
||||
|
||||
+129
-55
@@ -25,7 +25,12 @@ readonly STORAGE_HDD="hdd"
|
||||
readonly STORAGE_SAN="san"
|
||||
|
||||
# Auto-detect or use environment variables
|
||||
PG_DB_TYPE="${PG_DB_TYPE:-${DB_TYPE:-oltp}}"
|
||||
# NOTE: default profile is `mixed` (not `oltp`): Bonfire is OLTP plus a tail of analytics-shaped
|
||||
# queries (many-join boundarised feeds/threads), which is exactly PGTune's mixed workload. It also
|
||||
# right-sizes max_connections (100 vs oltp's 300 — Bonfire's Ecto pool is the pooler, ~25-50 conns;
|
||||
# an oversized value wastes RAM headroom and shrinks the computed work_mem, which caused measured
|
||||
# multi-TB temp-file spills in prod).
|
||||
PG_DB_TYPE="${PG_DB_TYPE:-${DB_TYPE:-mixed}}"
|
||||
PG_STORAGE_TYPE="${PG_STORAGE_TYPE:-${DISK_STORAGE_TYPE:-ssd}}"
|
||||
PG_DB_VERSION="${PG_DB_VERSION:-17}"
|
||||
|
||||
@@ -35,14 +40,21 @@ detect_total_memory_kb() {
|
||||
echo $((AVAILABLE_RAM_GB * GB / KB))
|
||||
elif [[ -n "${AVAILABLE_RAM_MB:-}" ]]; then
|
||||
echo $((AVAILABLE_RAM_MB * MB / KB))
|
||||
elif [[ -r /sys/fs/cgroup/memory.max && "$(cat /sys/fs/cgroup/memory.max)" != "max" ]]; then
|
||||
# cgroup v2: an explicit container memory limit is the deliberate budget signal
|
||||
echo $(($(cat /sys/fs/cgroup/memory.max) / KB))
|
||||
elif [[ -r /sys/fs/cgroup/memory/memory.limit_in_bytes ]] &&
|
||||
[[ "$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)" -lt $((1 << 60)) ]]; then
|
||||
# cgroup v1 (its "no limit" sentinel is a huge number rather than "max")
|
||||
echo $(($(cat /sys/fs/cgroup/memory/memory.limit_in_bytes) / KB))
|
||||
elif [[ -f /proc/meminfo ]]; then
|
||||
# Linux
|
||||
awk '/MemTotal/ {print int($2)}' /proc/meminfo
|
||||
# Calculate based on total available memory: claiming a fraction as co-hosted services need the rest. Sized for our standard stack (app + postgres + search + proxy). Override with PG_RAM_PERCENT or AVAILABLE_RAM_MB.
|
||||
awk -v pct="${PG_RAM_PERCENT:-40}" '/MemTotal/ {print int($2 * pct / 100)}' /proc/meminfo
|
||||
elif command -v sysctl &> /dev/null; then
|
||||
# macOS/BSD
|
||||
local mem_bytes
|
||||
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || sysctl -n hw.physmem 2>/dev/null)
|
||||
echo $((mem_bytes / KB))
|
||||
echo $((mem_bytes * ${PG_RAM_PERCENT:-40} / 100 / KB))
|
||||
else
|
||||
# Fallback: assume 4GB
|
||||
echo $((4 * GB / KB))
|
||||
@@ -284,33 +296,35 @@ calc_effective_io_concurrency() {
|
||||
}
|
||||
|
||||
# Calculate parallel workers settings
|
||||
# Echoes 4 space-separated values: max_worker_processes max_parallel_workers_per_gather max_parallel_workers max_parallel_maintenance_workers
|
||||
# (single source of truth — generate_env_vars reads these instead of duplicating the logic)
|
||||
calc_parallel_settings() {
|
||||
if [[ $CPU_COUNT -lt 4 ]]; then
|
||||
echo ""
|
||||
return
|
||||
local max_worker_processes max_parallel_workers_per_gather max_parallel_workers max_parallel_maintenance_workers
|
||||
|
||||
if [[ $CPU_COUNT -ge 4 ]]; then
|
||||
local workers_per_gather=$((CPU_COUNT / 2))
|
||||
# Cap at 4 for non-DW workloads
|
||||
if [[ "$PG_DB_TYPE" != "$DB_TYPE_DW" && $workers_per_gather -gt 4 ]]; then
|
||||
workers_per_gather=4
|
||||
fi
|
||||
|
||||
local parallel_maint_workers=$((CPU_COUNT / 2))
|
||||
if [[ $parallel_maint_workers -gt 4 ]]; then
|
||||
parallel_maint_workers=4
|
||||
fi
|
||||
|
||||
max_worker_processes=${PG_MAX_WORKER_PROCESSES:-$CPU_COUNT}
|
||||
max_parallel_workers_per_gather=${PG_MAX_PARALLEL_WORKERS_PER_GATHER:-$workers_per_gather}
|
||||
max_parallel_workers=${PG_MAX_PARALLEL_WORKERS:-$CPU_COUNT}
|
||||
max_parallel_maintenance_workers=${PG_MAX_PARALLEL_MAINTENANCE_WORKERS:-$parallel_maint_workers}
|
||||
else
|
||||
max_worker_processes=${PG_MAX_WORKER_PROCESSES:-8}
|
||||
max_parallel_workers_per_gather=${PG_MAX_PARALLEL_WORKERS_PER_GATHER:-2}
|
||||
max_parallel_workers=${PG_MAX_PARALLEL_WORKERS:-8}
|
||||
max_parallel_maintenance_workers=${PG_MAX_PARALLEL_MAINTENANCE_WORKERS:-2}
|
||||
fi
|
||||
|
||||
local workers_per_gather=$((CPU_COUNT / 2))
|
||||
local max_parallel_workers=${PG_MAX_PARALLEL_WORKERS:-$CPU_COUNT}
|
||||
local max_worker_processes=${PG_MAX_WORKER_PROCESSES:-$CPU_COUNT}
|
||||
|
||||
# Cap at 4 for non-DW workloads
|
||||
if [[ "$PG_DB_TYPE" != "$DB_TYPE_DW" && $workers_per_gather -gt 4 ]]; then
|
||||
workers_per_gather=4
|
||||
fi
|
||||
|
||||
local parallel_maint_workers=$((CPU_COUNT / 2))
|
||||
if [[ $parallel_maint_workers -gt 4 ]]; then
|
||||
parallel_maint_workers=4
|
||||
fi
|
||||
local max_parallel_maintenance_workers=${PG_MAX_PARALLEL_MAINTENANCE_WORKERS:-$parallel_maint_workers}
|
||||
|
||||
local max_parallel_workers_per_gather=${PG_MAX_PARALLEL_WORKERS_PER_GATHER:-$workers_per_gather}
|
||||
|
||||
echo "max_worker_processes = $max_worker_processes"
|
||||
echo "max_parallel_workers_per_gather = $max_parallel_workers_per_gather"
|
||||
echo "max_parallel_workers = $max_parallel_workers"
|
||||
echo "max_parallel_maintenance_workers = $max_parallel_maintenance_workers"
|
||||
echo "$max_worker_processes $max_parallel_workers_per_gather $max_parallel_workers $max_parallel_maintenance_workers"
|
||||
}
|
||||
|
||||
# Calculate work_mem
|
||||
@@ -322,8 +336,10 @@ calc_work_mem() {
|
||||
|
||||
local shared_buffers_kb=$1
|
||||
local max_connections=$2
|
||||
local max_worker_processes=${PG_MAX_WORKER_PROCESSES:-8}
|
||||
|
||||
# the actual computed worker count, passed in by generate_env_vars so the divisor stays
|
||||
# consistent with the exported max_worker_processes (was previously assumed to be 8)
|
||||
local max_worker_processes=${3:-${PG_MAX_WORKER_PROCESSES:-8}}
|
||||
|
||||
local work_mem_kb=$(( (TOTAL_MEMORY_KB - shared_buffers_kb) / ((max_connections + max_worker_processes) * 3) ))
|
||||
|
||||
case "$PG_DB_TYPE" in
|
||||
@@ -332,9 +348,17 @@ calc_work_mem() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# Minimum 64KB
|
||||
if [[ $work_mem_kb -lt 64 ]]; then
|
||||
work_mem_kb=64
|
||||
# Floor for web/oltp/mixed app workloads: below ~16MB, many-join queries (sorts + hashes)
|
||||
# constantly spill to disk-based algorithms writing temp files (measured multi-TB on a busy
|
||||
# instance at 16.6MB with an oversized max_connections divisor). Other profiles keep 64KB.
|
||||
local min_work_mem=64
|
||||
case "$PG_DB_TYPE" in
|
||||
"$DB_TYPE_WEB"|"$DB_TYPE_OLTP"|"$DB_TYPE_MIXED")
|
||||
min_work_mem=$((16 * MB / KB))
|
||||
;;
|
||||
esac
|
||||
if [[ $work_mem_kb -lt $min_work_mem ]]; then
|
||||
work_mem_kb=$min_work_mem
|
||||
fi
|
||||
|
||||
echo "$work_mem_kb"
|
||||
@@ -360,6 +384,48 @@ calc_checkpoint_completion_target() {
|
||||
echo "${PG_CHECKPOINT_COMPLETION_TARGET:-0.9}"
|
||||
}
|
||||
|
||||
# Calculate autovacuum_max_workers (~1/3 of cores, min 3 which is also the PG default)
|
||||
calc_autovacuum_max_workers() {
|
||||
if [[ -n "${PG_AUTOVACUUM_MAX_WORKERS:-}" ]]; then
|
||||
echo "$PG_AUTOVACUUM_MAX_WORKERS"
|
||||
return
|
||||
fi
|
||||
|
||||
local workers=$((CPU_COUNT / 3))
|
||||
if [[ $workers -lt 3 ]]; then
|
||||
workers=3
|
||||
fi
|
||||
echo "$workers"
|
||||
}
|
||||
|
||||
# WAL compression: lz4 is cheaper than the pglz default but needs PG >= 15
|
||||
calc_wal_compression() {
|
||||
if [[ -n "${PG_WAL_COMPRESSION:-}" ]]; then
|
||||
echo "$PG_WAL_COMPRESSION"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "${PG_DB_VERSION%%.*}" -ge 15 ]]; then
|
||||
echo "lz4"
|
||||
else
|
||||
echo "off"
|
||||
fi
|
||||
}
|
||||
|
||||
# TOAST compression for large values (post content etc.): lz4 needs PG >= 14
|
||||
calc_toast_compression() {
|
||||
if [[ -n "${PG_TOAST_COMPRESSION:-}" ]]; then
|
||||
echo "$PG_TOAST_COMPRESSION"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "${PG_DB_VERSION%%.*}" -ge 14 ]]; then
|
||||
echo "lz4"
|
||||
else
|
||||
echo "pglz"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main generation function - output as ENV vars
|
||||
generate_env_vars() {
|
||||
local max_connections shared_buffers_kb effective_cache_kb maint_work_mem_kb
|
||||
@@ -376,33 +442,20 @@ generate_env_vars() {
|
||||
default_stats_target=$(calc_default_statistics_target)
|
||||
random_page_cost=$(calc_random_page_cost)
|
||||
effective_io_concurrency=$(calc_effective_io_concurrency)
|
||||
work_mem_kb=$(calc_work_mem "$shared_buffers_kb" "$max_connections")
|
||||
huge_pages=$(calc_huge_pages)
|
||||
checkpoint_target=$(calc_checkpoint_completion_target)
|
||||
|
||||
# Calculate parallel settings as individual values
|
||||
# Parallel settings (single source: calc_parallel_settings), needed BEFORE work_mem
|
||||
# since the worker count is part of its divisor
|
||||
local max_worker_processes max_parallel_workers_per_gather max_parallel_workers max_parallel_maintenance_workers
|
||||
if [[ $CPU_COUNT -ge 4 ]]; then
|
||||
local workers_per_gather=$((CPU_COUNT / 2))
|
||||
if [[ "$PG_DB_TYPE" != "$DB_TYPE_DW" && $workers_per_gather -gt 4 ]]; then
|
||||
workers_per_gather=4
|
||||
fi
|
||||
|
||||
local parallel_maint_workers=$((CPU_COUNT / 2))
|
||||
if [[ $parallel_maint_workers -gt 4 ]]; then
|
||||
parallel_maint_workers=4
|
||||
fi
|
||||
read -r max_worker_processes max_parallel_workers_per_gather max_parallel_workers max_parallel_maintenance_workers <<< "$(calc_parallel_settings)"
|
||||
|
||||
max_worker_processes=${PG_MAX_WORKER_PROCESSES:-$CPU_COUNT}
|
||||
max_parallel_workers_per_gather=${PG_MAX_PARALLEL_WORKERS_PER_GATHER:-$workers_per_gather}
|
||||
max_parallel_workers=${PG_MAX_PARALLEL_WORKERS:-$CPU_COUNT}
|
||||
max_parallel_maintenance_workers=${PG_MAX_PARALLEL_MAINTENANCE_WORKERS:-$parallel_maint_workers}
|
||||
else
|
||||
max_worker_processes=${PG_MAX_WORKER_PROCESSES:-8}
|
||||
max_parallel_workers_per_gather=${PG_MAX_PARALLEL_WORKERS_PER_GATHER:-2}
|
||||
max_parallel_workers=${PG_MAX_PARALLEL_WORKERS:-8}
|
||||
max_parallel_maintenance_workers=${PG_MAX_PARALLEL_MAINTENANCE_WORKERS:-2}
|
||||
fi
|
||||
work_mem_kb=$(calc_work_mem "$shared_buffers_kb" "$max_connections" "$max_worker_processes")
|
||||
|
||||
local autovacuum_max_workers wal_compression toast_compression
|
||||
autovacuum_max_workers=$(calc_autovacuum_max_workers)
|
||||
wal_compression=$(calc_wal_compression)
|
||||
toast_compression=$(calc_toast_compression)
|
||||
|
||||
# Output ENV vars (only if not already set)
|
||||
cat << EOF
|
||||
@@ -424,6 +477,27 @@ export PG_MAX_WORKER_PROCESSES=\${PG_MAX_WORKER_PROCESSES:-$max_worker_processes
|
||||
export PG_MAX_PARALLEL_WORKERS_PER_GATHER=\${PG_MAX_PARALLEL_WORKERS_PER_GATHER:-$max_parallel_workers_per_gather}
|
||||
export PG_MAX_PARALLEL_WORKERS=\${PG_MAX_PARALLEL_WORKERS:-$max_parallel_workers}
|
||||
export PG_MAX_PARALLEL_MAINTENANCE_WORKERS=\${PG_MAX_PARALLEL_MAINTENANCE_WORKERS:-$max_parallel_maintenance_workers}
|
||||
# JIT: a per-execution LLVM compile tax on complex-but-fast app queries; off unless doing long analytics scans
|
||||
export PG_JIT=\${PG_JIT:-off}
|
||||
# observability: query stats extension (restart-required to change), I/O timing, full query texts in pg_stat_activity
|
||||
export PG_PRELOAD_LIBS=\${PG_PRELOAD_LIBS:-pg_stat_statements}
|
||||
export PG_TRACK_IO_TIMING=\${PG_TRACK_IO_TIMING:-on}
|
||||
export PG_TRACK_ACTIVITY_QUERY_SIZE=\${PG_TRACK_ACTIVITY_QUERY_SIZE:-16384}
|
||||
# autovacuum tuned for soft-delete/append app workloads (default 20% thresholds never trigger on big tables);
|
||||
# per-table thresholds for the hottest tables also ship as an app migration
|
||||
export PG_AUTOVACUUM_VACUUM_SCALE_FACTOR=\${PG_AUTOVACUUM_VACUUM_SCALE_FACTOR:-0.05}
|
||||
export PG_AUTOVACUUM_VACUUM_INSERT_SCALE_FACTOR=\${PG_AUTOVACUUM_VACUUM_INSERT_SCALE_FACTOR:-0.05}
|
||||
export PG_AUTOVACUUM_ANALYZE_SCALE_FACTOR=\${PG_AUTOVACUUM_ANALYZE_SCALE_FACTOR:-0.02}
|
||||
export PG_AUTOVACUUM_VACUUM_COST_LIMIT=\${PG_AUTOVACUUM_VACUUM_COST_LIMIT:-1000}
|
||||
export PG_AUTOVACUUM_MAX_WORKERS=\${PG_AUTOVACUUM_MAX_WORKERS:-$autovacuum_max_workers}
|
||||
export PG_LOG_AUTOVACUUM_MIN_DURATION=\${PG_LOG_AUTOVACUUM_MIN_DURATION:-0}
|
||||
# cheaper compression codecs (version-gated: wal lz4 needs PG15+, toast lz4 PG14+)
|
||||
export PG_WAL_COMPRESSION=\${PG_WAL_COMPRESSION:-$wal_compression}
|
||||
export PG_TOAST_COMPRESSION=\${PG_TOAST_COMPRESSION:-$toast_compression}
|
||||
# diagnostics logging: slow statements (server-side; the app logs its own from 100ms), lock waits, temp-file spills
|
||||
export PG_LOG_MIN_DURATION_STATEMENT=\${PG_LOG_MIN_DURATION_STATEMENT:-2000}
|
||||
export PG_LOG_LOCK_WAITS=\${PG_LOG_LOCK_WAITS:-on}
|
||||
export PG_LOG_TEMP_FILES=\${PG_LOG_TEMP_FILES:-0}
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
@@ -224,6 +224,7 @@ wal_buffers = ${PG_WAL_BUFFERS_MB}MB # min 32kB, -1 sets based on shared_buffers
|
||||
#full_page_writes = on # recover from partial page writes
|
||||
#wal_log_hints = off # also do full page writes of non-critical updates
|
||||
# (change requires restart)
|
||||
wal_compression = ${PG_WAL_COMPRESSION} # cheaper full-page writes (lz4 on PG15+; computed by postgres-tune.sh)
|
||||
#wal_compression = off # enables compression of full-page writes;
|
||||
# off, pglz, lz4, zstd, or on
|
||||
#wal_init_zero = on # zero-fill new WAL files
|
||||
@@ -430,6 +431,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
#constraint_exclusion = partition # on, off, or partition
|
||||
#cursor_tuple_fraction = 0.1 # range 0.0-1.0
|
||||
#from_collapse_limit = 8
|
||||
jit = ${PG_JIT} # per-execution LLVM compile tax on complex-but-fast app queries; off by default (tune with PG_JIT)
|
||||
#jit = on # allow JIT compilation
|
||||
#join_collapse_limit = 8 # 1 disables collapsing of explicit
|
||||
# JOIN clauses
|
||||
@@ -517,6 +519,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
# fatal
|
||||
# panic (effectively off)
|
||||
|
||||
log_min_duration_statement = ${PG_LOG_MIN_DURATION_STATEMENT} # server-side slow-statement log (the app logs its own from 100ms)
|
||||
#log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
|
||||
# and their durations, > 0 logs only
|
||||
# statements running at least this number
|
||||
@@ -548,6 +551,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
#debug_print_rewritten = off
|
||||
#debug_print_plan = off
|
||||
#debug_pretty_print = on
|
||||
log_autovacuum_min_duration = ${PG_LOG_AUTOVACUUM_MIN_DURATION} # log every autovacuum run — the regression tripwire for autovacuum-never-runs
|
||||
#log_autovacuum_min_duration = 10min # log autovacuum activity;
|
||||
# -1 disables, 0 logs all actions and
|
||||
# their durations, > 0 logs only
|
||||
@@ -583,6 +587,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
# processes
|
||||
# %% = '%'
|
||||
# e.g. '<%u%%%d> '
|
||||
log_lock_waits = ${PG_LOG_LOCK_WAITS}
|
||||
#log_lock_waits = off # log lock waits >= deadlock_timeout
|
||||
#log_recovery_conflict_waits = off # log standby recovery conflict waits
|
||||
# >= deadlock_timeout
|
||||
@@ -594,6 +599,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
# -1 means print in full, 0 disables
|
||||
#log_statement = 'none' # none, ddl, mod, all
|
||||
#log_replication_commands = off
|
||||
log_temp_files = ${PG_LOG_TEMP_FILES} # log all temp-file spills (the work_mem-too-small signal)
|
||||
#log_temp_files = -1 # log temporary files equal or larger
|
||||
# than the specified size in kilobytes;
|
||||
# -1 disables, 0 logs all temp files
|
||||
@@ -613,8 +619,10 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
# - Cumulative Query and Index Statistics -
|
||||
|
||||
#track_activities = on
|
||||
track_activity_query_size = ${PG_TRACK_ACTIVITY_QUERY_SIZE} # full query texts in pg_stat_activity (change requires restart)
|
||||
#track_activity_query_size = 1024 # (change requires restart)
|
||||
#track_counts = on
|
||||
track_io_timing = ${PG_TRACK_IO_TIMING} # needed for pg_stat_statements I/O attribution
|
||||
#track_io_timing = off
|
||||
#track_wal_io_timing = off
|
||||
#track_functions = none # none, pl, all
|
||||
@@ -636,6 +644,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
|
||||
#autovacuum = on # Enable autovacuum subprocess? 'on'
|
||||
# requires track_counts to also be on.
|
||||
autovacuum_max_workers = ${PG_AUTOVACUUM_MAX_WORKERS} # (change requires restart)
|
||||
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
|
||||
# (change requires restart)
|
||||
#autovacuum_naptime = 1min # time between autovacuum runs
|
||||
@@ -646,9 +655,12 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
# vacuums
|
||||
#autovacuum_analyze_threshold = 50 # min number of row updates before
|
||||
# analyze
|
||||
autovacuum_vacuum_scale_factor = ${PG_AUTOVACUUM_VACUUM_SCALE_FACTOR} # default 0.2 never triggers on big tables
|
||||
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
|
||||
autovacuum_vacuum_insert_scale_factor = ${PG_AUTOVACUUM_VACUUM_INSERT_SCALE_FACTOR} # key for append-mostly workloads: keeps visibility maps fresh for index-only scans
|
||||
#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table
|
||||
# size before insert vacuum
|
||||
autovacuum_analyze_scale_factor = ${PG_AUTOVACUUM_ANALYZE_SCALE_FACTOR}
|
||||
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
|
||||
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
|
||||
# (change requires restart)
|
||||
@@ -658,6 +670,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
|
||||
# autovacuum, in milliseconds;
|
||||
# -1 means use vacuum_cost_delay
|
||||
autovacuum_vacuum_cost_limit = ${PG_AUTOVACUUM_VACUUM_COST_LIMIT} # default (200 via vacuum_cost_limit) is glacial on multi-GB tables
|
||||
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
|
||||
# autovacuum, -1 means use
|
||||
# vacuum_cost_limit
|
||||
@@ -683,6 +696,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
#row_security = on
|
||||
#default_table_access_method = 'heap'
|
||||
#default_tablespace = '' # a tablespace name, '' uses the default
|
||||
default_toast_compression = ${PG_TOAST_COMPRESSION} # lz4 on PG14+ (computed by postgres-tune.sh)
|
||||
#default_toast_compression = 'pglz' # 'pglz' or 'lz4'
|
||||
#temp_tablespaces = '' # a list of tablespace names, '' uses
|
||||
# only default tablespace
|
||||
@@ -741,6 +755,7 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
|
||||
#local_preload_libraries = ''
|
||||
#session_preload_libraries = ''
|
||||
shared_preload_libraries = '${PG_PRELOAD_LIBS}' # pg_stat_statements etc. (change requires restart)
|
||||
#shared_preload_libraries = '' # (change requires restart)
|
||||
#jit_provider = 'llvmjit' # JIT library to use
|
||||
|
||||
@@ -817,3 +832,4 @@ default_statistics_target = ${PG_DEFAULT_STATISTICS_TARGET} # range 1-10000
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Add settings for extensions here
|
||||
pg_stat_statements.track = all
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ATTENTION: this is the first release and contains a couple of breaking changes in comparison to the previously unreleased recipe. There are a lot of secrets, keys and passwords moved to docker secrets, and the .env.sample was completly restructured. It is recommended to start your env file from scratch from the new template to make sure nothing is missing.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Sonic configuration
|
||||
# https://github.com/valeriansaliou/sonic/blob/master/CONFIGURATION.md
|
||||
|
||||
[server]
|
||||
log_level = "error"
|
||||
|
||||
[channel]
|
||||
inet = "0.0.0.0:1491"
|
||||
tcp_timeout = 300
|
||||
|
||||
[channel.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/"
|
||||
retain_word_objects = 1000
|
||||
|
||||
[store.kv.pool]
|
||||
inactive_after = 1800
|
||||
|
||||
[store.kv.database]
|
||||
flush_after = 900
|
||||
compress = true
|
||||
parallelism = 2
|
||||
max_files = 100
|
||||
max_compactions = 1
|
||||
max_flushes = 1
|
||||
write_buffer = 16384
|
||||
write_ahead_log = true
|
||||
|
||||
[store.fst]
|
||||
path = "/var/lib/sonic/store/fst/"
|
||||
|
||||
[store.fst.pool]
|
||||
inactive_after = 300
|
||||
|
||||
[store.fst.graph]
|
||||
consolidate_after = 180
|
||||
max_size = 2048
|
||||
max_words = 250000
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
export SONIC_CHANNEL__AUTH_PASSWORD="$(cat /run/secrets/sonic_password)"
|
||||
|
||||
exec sonic -c /etc/sonic.cfg
|
||||
Reference in New Issue
Block a user