From a2bf4b1393b974abee3dd94a0a81d842ba04c84b Mon Sep 17 00:00:00 2001 From: Akshat Mehta Date: Wed, 8 Jul 2026 01:10:34 +0530 Subject: [PATCH 01/55] fix(vaultwarden): update outdated image version (#921) --- blueprints/vaultwarden/docker-compose.yml | 2 +- meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blueprints/vaultwarden/docker-compose.yml b/blueprints/vaultwarden/docker-compose.yml index 5b2f14e3..6998809c 100644 --- a/blueprints/vaultwarden/docker-compose.yml +++ b/blueprints/vaultwarden/docker-compose.yml @@ -1,7 +1,7 @@ # the webserver can take a while to start up, so don't be alarmed if it takes a few minutes to get a response services: vaultwarden: - image: vaultwarden/server:1.34.3 + image: vaultwarden/server:1.36.0 restart: always environment: DOMAIN: ${DOMAIN} diff --git a/meta.json b/meta.json index f1dbb9fe..4afcfd83 100644 --- a/meta.json +++ b/meta.json @@ -6712,7 +6712,7 @@ { "id": "vaultwarden", "name": "Vaultwarden", - "version": "1.34.3", + "version": "1.36.0", "description": "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", "logo": "vaultwarden.svg", "links": { From 5bbc380f36ba534b4382c6354c568ed188d7ebd2 Mon Sep 17 00:00:00 2001 From: Mendy Landa <54242706+MendyLanda@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:41:57 +0300 Subject: [PATCH 02/55] Add Cut (self-hosted URL shortener) template (#914) --- blueprints/cut/cut.svg | 10 +++++++++ blueprints/cut/docker-compose.yml | 37 +++++++++++++++++++++++++++++++ blueprints/cut/template.toml | 14 ++++++++++++ meta.json | 16 +++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 blueprints/cut/cut.svg create mode 100644 blueprints/cut/docker-compose.yml create mode 100644 blueprints/cut/template.toml diff --git a/blueprints/cut/cut.svg b/blueprints/cut/cut.svg new file mode 100644 index 00000000..c0604eac --- /dev/null +++ b/blueprints/cut/cut.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/blueprints/cut/docker-compose.yml b/blueprints/cut/docker-compose.yml new file mode 100644 index 00000000..b31578d1 --- /dev/null +++ b/blueprints/cut/docker-compose.yml @@ -0,0 +1,37 @@ +services: + cut: + image: ghcr.io/mendylanda/cut:latest + restart: unless-stopped + depends_on: + redis: + condition: service_healthy + environment: + # Injected by Dokploy from template.toml (generated strong password). + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + # Bundled Redis below, private to the project network. + - REDIS_URL=redis://redis:6379 + # Expose the container port only; Dokploy maps the domain to it. + ports: + - 3000 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] + interval: 10s + timeout: 5s + retries: 10 + + redis: + image: redis:7-alpine + restart: unless-stopped + # appendonly = durable across restarts; noeviction because this is Cut's + # primary datastore, not a cache (don't drop links under memory pressure). + command: redis-server --appendonly yes --maxmemory-policy noeviction + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 10 + +volumes: + redis-data: diff --git a/blueprints/cut/template.toml b/blueprints/cut/template.toml new file mode 100644 index 00000000..83de686c --- /dev/null +++ b/blueprints/cut/template.toml @@ -0,0 +1,14 @@ +[variables] +main_domain = "${domain}" +admin_password = "${password:24}" + +[config] +env = [ + "ADMIN_PASSWORD=${admin_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "cut" +port = 3000 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 4afcfd83..7c6451dd 100644 --- a/meta.json +++ b/meta.json @@ -1732,6 +1732,22 @@ "self-hosted" ] }, + { + "id": "cut", + "name": "Cut", + "version": "0.1.0", + "description": "A tiny, self-hosted URL shortener — short links that are entirely yours, with per-link passwords, expiries, and click limits.", + "logo": "cut.svg", + "links": { + "github": "https://github.com/MendyLanda/cut", + "website": "https://github.com/MendyLanda/cut", + "docs": "https://github.com/MendyLanda/cut#readme" + }, + "tags": [ + "url-shortener", + "links" + ] + }, { "id": "cyberchef", "name": "CyberChef", From a3d1efad02e0b85488552a801597534f613a1e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Carvajal?= <66692533+ncarvajalc@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:42:42 -0500 Subject: [PATCH 03/55] chore: update hi-events template to version 1.9.0-beta and add smtp mail configuration (#911) --- blueprints/hi-events/docker-compose.yml | 4 +++- blueprints/hi-events/template.toml | 2 ++ meta.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/blueprints/hi-events/docker-compose.yml b/blueprints/hi-events/docker-compose.yml index 3c2291b9..dd3d7269 100644 --- a/blueprints/hi-events/docker-compose.yml +++ b/blueprints/hi-events/docker-compose.yml @@ -1,6 +1,6 @@ services: all-in-one: - image: daveearley/hi.events-all-in-one:v0.8.0-beta.1 + image: daveearley/hi.events-all-in-one:v1.9.0-beta restart: always environment: - VITE_FRONTEND_URL=https://${DOMAIN} @@ -20,6 +20,8 @@ services: - MAIL_MAILER - MAIL_HOST - MAIL_PORT + - MAIL_USERNAME + - MAIL_PASSWORD - MAIL_FROM_ADDRESS - MAIL_FROM_NAME depends_on: diff --git a/blueprints/hi-events/template.toml b/blueprints/hi-events/template.toml index 4fea86ce..a8c2c3d3 100644 --- a/blueprints/hi-events/template.toml +++ b/blueprints/hi-events/template.toml @@ -16,6 +16,8 @@ env = [ "MAIL_MAILER=", "MAIL_HOST=", "MAIL_PORT=", + "MAIL_USERNAME=", + "MAIL_PASSWORD=", "MAIL_FROM_ADDRESS=", "MAIL_FROM_NAME=", ] diff --git a/meta.json b/meta.json index 7c6451dd..7ac97b0b 100644 --- a/meta.json +++ b/meta.json @@ -3111,7 +3111,7 @@ { "id": "hi-events", "name": "Hi.events", - "version": "0.8.0-beta.1", + "version": "1.9.0-beta", "description": "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", "logo": "hi-events.svg", "links": { From bdda9b476be3ab25027e4536f439bbd312fc0504 Mon Sep 17 00:00:00 2001 From: Shabani Magawila <36422571+ShabaniMagawila@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:44:45 +0300 Subject: [PATCH 04/55] feat: Add GeoServer & PostGIS blueprint (#919) * Add GeoServer & PostGIS blueprint * Add GeoServer blueprint metadata and logo * Fix meta.json formatting and sorting * Fix template.toml schema validation errors * Remove invalid config.mounts from template.toml --- blueprints/geoserver/docker-compose.yml | 27 ++++++++++++++++++++++++ blueprints/geoserver/logo.png | Bin 0 -> 16349 bytes blueprints/geoserver/logo.svg | 21 ++++++++++++++++++ blueprints/geoserver/template.toml | 18 ++++++++++++++++ meta.json | 17 +++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 blueprints/geoserver/docker-compose.yml create mode 100644 blueprints/geoserver/logo.png create mode 100644 blueprints/geoserver/logo.svg create mode 100644 blueprints/geoserver/template.toml diff --git a/blueprints/geoserver/docker-compose.yml b/blueprints/geoserver/docker-compose.yml new file mode 100644 index 00000000..fdd4fc97 --- /dev/null +++ b/blueprints/geoserver/docker-compose.yml @@ -0,0 +1,27 @@ +services: + geoserver: + image: kartoza/geoserver:2.24.1 + depends_on: + - db + environment: + - GEOSERVER_ADMIN_USER=${GEOSERVER_ADMIN_USER} + - GEOSERVER_ADMIN_PASSWORD=${GEOSERVER_ADMIN_PASSWORD} + - INITIAL_MEMORY=${INITIAL_MEMORY} + - MAXIMUM_MEMORY=${MAXIMUM_MEMORY} + volumes: + - geoserver-data:/opt/geoserver/data_dir + restart: unless-stopped + + db: + image: postgis/postgis:17-3.4 + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + volumes: + - postgis-data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + geoserver-data: + postgis-data: diff --git a/blueprints/geoserver/logo.png b/blueprints/geoserver/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..14ae15a6a465dd9346acb2f527e48ce6fad517c8 GIT binary patch literal 16349 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 + + + + + + + + + + + \ No newline at end of file diff --git a/blueprints/geoserver/template.toml b/blueprints/geoserver/template.toml new file mode 100644 index 00000000..a975cc8e --- /dev/null +++ b/blueprints/geoserver/template.toml @@ -0,0 +1,18 @@ +[info] +name = "GeoServer & PostGIS" +description = "An open-source server for sharing geospatial data, paired with a PostGIS database. Template created and maintained by OpenGeoCity Tanzania (info@ogctz.org). For further assistance contact +255759968919 or visit https://opengeocity.org or github: https://github.com/ShabaniMagawila or linkedin: https://www.linkedin.com/in/shabani-magawila/" +version = "2.24.1" + +[variables] +GEOSERVER_ADMIN_USER = "admin" +GEOSERVER_ADMIN_PASSWORD = "geoserver_secure" +INITIAL_MEMORY = "2G" +MAXIMUM_MEMORY = "4G" +POSTGRES_USER = "postgres" +POSTGRES_PASSWORD = "postgres_secure" +POSTGRES_DB = "gis" + +[config] +[[config.domains]] +serviceName = "geoserver" +port = 8080 \ No newline at end of file diff --git a/meta.json b/meta.json index 7ac97b0b..ffaa70e2 100644 --- a/meta.json +++ b/meta.json @@ -2753,6 +2753,23 @@ "object-storage" ] }, + { + "id": "geoserver", + "name": "GeoServer & PostGIS", + "version": "2.24.1", + "description": "An open-source server for sharing geospatial data, paired with a PostGIS database. Template created and maintained by OpenGeoCity Tanzania (info@ogctz.org). For further assistance contact +255759968919 or visit https://opengeocity.org or github: https://github.com/ShabaniMagawila or linkedin: https://www.linkedin.com/in/shabani-magawila/", + "logo": "logo.png", + "links": { + "github": "https://github.com/geoserver/geoserver", + "website": "https://geoserver.org/", + "docs": "https://docs.geoserver.org/" + }, + "tags": [ + "geospatial", + "gis", + "postgis" + ] + }, { "id": "ghost", "name": "Ghost", From cf862fccfb40b0e3a911bcf2bb95f30e60f17ab1 Mon Sep 17 00:00:00 2001 From: MANFIT7 <123054818+MANFIT7@users.noreply.github.com> Date: Wed, 8 Jul 2026 03:53:26 +0800 Subject: [PATCH 05/55] feat: add LibreSpeed template (#899) --- blueprints/librespeed/docker-compose.yml | 20 ++++++++++++++++++++ blueprints/librespeed/librespeed.svg | 1 + blueprints/librespeed/template.toml | 14 ++++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 blueprints/librespeed/docker-compose.yml create mode 100644 blueprints/librespeed/librespeed.svg create mode 100644 blueprints/librespeed/template.toml diff --git a/blueprints/librespeed/docker-compose.yml b/blueprints/librespeed/docker-compose.yml new file mode 100644 index 00000000..2f7e8bc1 --- /dev/null +++ b/blueprints/librespeed/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + librespeed: + image: lscr.io/linuxserver/librespeed:latest + restart: unless-stopped + expose: + - 80 + volumes: + - librespeed-config:/config + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TZ} + - PASSWORD=${PASSWORD} + - CUSTOM_RESULTS=${CUSTOM_RESULTS} + - DB_TYPE=sqlite + +volumes: + librespeed-config: diff --git a/blueprints/librespeed/librespeed.svg b/blueprints/librespeed/librespeed.svg new file mode 100644 index 00000000..c76c99ec --- /dev/null +++ b/blueprints/librespeed/librespeed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/librespeed/template.toml b/blueprints/librespeed/template.toml new file mode 100644 index 00000000..b8ed3e13 --- /dev/null +++ b/blueprints/librespeed/template.toml @@ -0,0 +1,14 @@ +[variables] +main_domain = "${domain}" +password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "librespeed" +port = 80 +host = "${main_domain}" + +[config.env] +PASSWORD = "${password}" +TZ = "UTC" +CUSTOM_RESULTS = "false" diff --git a/meta.json b/meta.json index ffaa70e2..c16a0404 100644 --- a/meta.json +++ b/meta.json @@ -3846,6 +3846,24 @@ "object-storage" ] }, + { + "id": "librespeed", + "name": "LibreSpeed", + "version": "latest", + "description": "LibreSpeed is a lightweight, self-hosted HTML5 speed test for measuring download, upload, ping, and jitter in modern browsers.", + "logo": "librespeed.svg", + "links": { + "github": "https://github.com/librespeed/speedtest", + "website": "https://librespeed.org", + "docs": "https://docs.linuxserver.io/images/docker-librespeed/" + }, + "tags": [ + "speedtest", + "networking", + "monitoring", + "self-hosted" + ] + }, { "id": "libretranslate", "name": "LibreTranslate", From 76ceefdd2b7ae5d7e5149a04318a7ef41c34fa45 Mon Sep 17 00:00:00 2001 From: MANFIT7 <123054818+MANFIT7@users.noreply.github.com> Date: Wed, 8 Jul 2026 03:55:14 +0800 Subject: [PATCH 06/55] feat: add Snipe-IT template (#897) --- blueprints/snipe-it/docker-compose.yml | 68 +++++++++++++++++++++++++ blueprints/snipe-it/snipe-it.png | Bin 0 -> 22482 bytes blueprints/snipe-it/template.toml | 25 +++++++++ meta.json | 18 +++++++ 4 files changed, 111 insertions(+) create mode 100644 blueprints/snipe-it/docker-compose.yml create mode 100644 blueprints/snipe-it/snipe-it.png create mode 100644 blueprints/snipe-it/template.toml diff --git a/blueprints/snipe-it/docker-compose.yml b/blueprints/snipe-it/docker-compose.yml new file mode 100644 index 00000000..be3140b7 --- /dev/null +++ b/blueprints/snipe-it/docker-compose.yml @@ -0,0 +1,68 @@ +version: "3.8" + +services: + snipe-it: + image: snipe/snipe-it:v8.5.0 + restart: unless-stopped + expose: + - 80 + volumes: + - snipe-it-storage:/var/lib/snipeit + environment: + - APP_ENV=production + - APP_DEBUG=false + - APP_KEY=${APP_KEY} + - APP_URL=${APP_URL} + - APP_TIMEZONE=${APP_TIMEZONE} + - APP_LOCALE=${APP_LOCALE} + - APP_TRUSTED_PROXIES=${APP_TRUSTED_PROXIES} + - APP_FORCE_TLS=${APP_FORCE_TLS} + - SECURE_COOKIES=${SECURE_COOKIES} + - PRIVATE_FILESYSTEM_DISK=local + - PUBLIC_FILESYSTEM_DISK=local_public + - DB_CONNECTION=mysql + - DB_HOST=snipe-it-db + - DB_PORT=3306 + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - DB_PREFIX=null + - DB_DUMP_PATH=/usr/bin + - DB_DUMP_SKIP_SSL=true + - DB_CHARSET=utf8mb4 + - DB_COLLATION=utf8mb4_unicode_ci + - MAIL_MAILER=log + - MAIL_FROM_ADDR=${MAIL_FROM_ADDR} + - MAIL_FROM_NAME=Snipe-IT + - MAIL_REPLYTO_ADDR=${MAIL_FROM_ADDR} + - MAIL_REPLYTO_NAME=Snipe-IT + - ALLOW_BACKUP_DELETE=false + - ALLOW_DATA_PURGE=false + - IMAGE_LIB=gd + - LOG_CHANNEL=stderr + - CACHE_DRIVER=file + - SESSION_DRIVER=file + - QUEUE_DRIVER=sync + depends_on: + snipe-it-db: + condition: service_healthy + + snipe-it-db: + image: mariadb:11.4.7 + restart: unless-stopped + volumes: + - snipe-it-db-data:/var/lib/mysql + environment: + - MYSQL_DATABASE=${DB_DATABASE} + - MYSQL_USER=${DB_USERNAME} + - MYSQL_PASSWORD=${DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 5s + timeout: 2s + retries: 10 + +volumes: + snipe-it-storage: + snipe-it-db-data: diff --git a/blueprints/snipe-it/snipe-it.png b/blueprints/snipe-it/snipe-it.png new file mode 100644 index 0000000000000000000000000000000000000000..423a5b74906c4a1f67dab665a59a6731f0dbaa98 GIT binary patch literal 22482 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 literal 0 HcmV?d00001 diff --git a/blueprints/snipe-it/template.toml b/blueprints/snipe-it/template.toml new file mode 100644 index 00000000..aca258a5 --- /dev/null +++ b/blueprints/snipe-it/template.toml @@ -0,0 +1,25 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" +db_password = "${password:24}" +mysql_root_password = "${password:24}" + +[config] +[[config.domains]] +serviceName = "snipe-it" +port = 80 +host = "${main_domain}" + +[config.env] +APP_KEY = "base64:${app_key}" +APP_URL = "https://${main_domain}" +APP_TIMEZONE = "UTC" +APP_LOCALE = "en-US" +APP_TRUSTED_PROXIES = "172.0.0.0/8" +APP_FORCE_TLS = "true" +SECURE_COOKIES = "true" +DB_DATABASE = "snipeit" +DB_USERNAME = "snipeit" +DB_PASSWORD = "${db_password}" +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" +MAIL_FROM_ADDR = "snipe-it@${main_domain}" diff --git a/meta.json b/meta.json index c16a0404..5dae84df 100644 --- a/meta.json +++ b/meta.json @@ -6108,6 +6108,24 @@ "authentication" ] }, + { + "id": "snipe-it", + "name": "Snipe-IT", + "version": "v8.5.0", + "description": "Snipe-IT is an open-source asset management system for tracking hardware, software licenses, accessories, and consumables.", + "logo": "snipe-it.png", + "links": { + "github": "https://github.com/grokability/snipe-it", + "website": "https://snipeitapp.com/", + "docs": "https://snipe-it.readme.io/docs" + }, + "tags": [ + "asset-management", + "inventory", + "business", + "self-hosted" + ] + }, { "id": "soketi", "name": "Soketi", From e83e869bd3b6b030a7a22924f62c6a4acf7df735 Mon Sep 17 00:00:00 2001 From: Kokoro <100moreplus1@proton.me> Date: Tue, 7 Jul 2026 21:55:48 +0200 Subject: [PATCH 07/55] feat: add LubeLogger template (#896) Co-authored-by: 100more <184098138+100more@users.noreply.github.com> --- blueprints/lubelogger/docker-compose.yml | 20 ++++++++++++++++++++ blueprints/lubelogger/lubelogger.png | Bin 0 -> 8262 bytes blueprints/lubelogger/template.toml | 18 ++++++++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 blueprints/lubelogger/docker-compose.yml create mode 100644 blueprints/lubelogger/lubelogger.png create mode 100644 blueprints/lubelogger/template.toml diff --git a/blueprints/lubelogger/docker-compose.yml b/blueprints/lubelogger/docker-compose.yml new file mode 100644 index 00000000..535b8e55 --- /dev/null +++ b/blueprints/lubelogger/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + lubelogger: + image: ghcr.io/hargata/lubelogger:v1.6.5 + restart: unless-stopped + environment: + - LC_ALL=${LUBELOGGER_LOCALE} + - LANG=${LUBELOGGER_LOCALE} + - LUBELOGGER_DOMAIN=${LUBELOGGER_DOMAIN} + - EnableAuth=${LUBELOGGER_ENABLE_AUTH} + volumes: + - lubelogger-data:/App/data + - lubelogger-keys:/root/.aspnet/DataProtection-Keys + expose: + - 8080 + +volumes: + lubelogger-data: {} + lubelogger-keys: {} diff --git a/blueprints/lubelogger/lubelogger.png b/blueprints/lubelogger/lubelogger.png new file mode 100644 index 0000000000000000000000000000000000000000..b3713e8067a37d3e7a04f8d777add1d4c4c27d1b GIT binary patch literal 8262 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 Eg0dmvRR910 literal 0 HcmV?d00001 diff --git a/blueprints/lubelogger/template.toml b/blueprints/lubelogger/template.toml new file mode 100644 index 00000000..4ff28963 --- /dev/null +++ b/blueprints/lubelogger/template.toml @@ -0,0 +1,18 @@ +[variables] +main_domain = "${domain}" +lubelogger_locale = "en_US.UTF-8" +lubelogger_enable_auth = "false" +lubelogger_domain = "https://${main_domain}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "lubelogger" +port = 8080 +host = "${main_domain}" + +[config.env] +LUBELOGGER_LOCALE = "${lubelogger_locale}" +LUBELOGGER_ENABLE_AUTH = "${lubelogger_enable_auth}" +LUBELOGGER_DOMAIN = "${lubelogger_domain}" diff --git a/meta.json b/meta.json index 5dae84df..86705cb4 100644 --- a/meta.json +++ b/meta.json @@ -4053,6 +4053,24 @@ "development" ] }, + { + "id": "lubelogger", + "name": "LubeLogger", + "version": "v1.6.5", + "description": "LubeLogger is a self-hosted vehicle maintenance and fuel mileage tracker for managing service records, reminders, fuel logs, and vehicle expenses.", + "logo": "lubelogger.png", + "links": { + "github": "https://github.com/hargata/lubelog", + "website": "https://lubelogger.com/", + "docs": "https://docs.lubelogger.com/" + }, + "tags": [ + "vehicle", + "maintenance", + "fleet", + "self-hosted" + ] + }, { "id": "macos", "name": "MacOS (dockerized)", From c70cd23b7edee4edebefe5ef68389dcbd3a71de6 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:01:01 +0400 Subject: [PATCH 08/55] feat: add Paperclip template (#943) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/paperclip/README.md | 15 +++++++++ blueprints/paperclip/docker-compose.yml | 44 +++++++++++++++++++++++++ blueprints/paperclip/logo.svg | 5 +++ blueprints/paperclip/template.toml | 20 +++++++++++ meta.json | 18 ++++++++++ 5 files changed, 102 insertions(+) create mode 100644 blueprints/paperclip/README.md create mode 100644 blueprints/paperclip/docker-compose.yml create mode 100644 blueprints/paperclip/logo.svg create mode 100644 blueprints/paperclip/template.toml diff --git a/blueprints/paperclip/README.md b/blueprints/paperclip/README.md new file mode 100644 index 00000000..2ccae853 --- /dev/null +++ b/blueprints/paperclip/README.md @@ -0,0 +1,15 @@ +# Paperclip + +Paperclip is an open-source control plane for managing teams of AI agents, goals, budgets, tickets, and recurring work. + +This Dokploy template includes: + +- Paperclip web/API service using the official GHCR image +- PostgreSQL 17 database with health check +- Generated `BETTER_AUTH_SECRET` +- Generated database password +- Persistent `/paperclip` storage for instance data +- Public URL configured from the Dokploy domain +- Dokploy domain routing to Paperclip on port 3100 + +After deployment, check the Paperclip container logs for first-run setup or invite information. \ No newline at end of file diff --git a/blueprints/paperclip/docker-compose.yml b/blueprints/paperclip/docker-compose.yml new file mode 100644 index 00000000..5c1ce7bf --- /dev/null +++ b/blueprints/paperclip/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: paperclip + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: paperclip + healthcheck: + test: ["CMD-SHELL", "pg_isready -U paperclip -d paperclip"] + interval: 10s + timeout: 5s + retries: 10 + volumes: + - postgres-data:/var/lib/postgresql/data + + paperclip: + image: ghcr.io/paperclipai/paperclip:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + HOST: 0.0.0.0 + PORT: "3100" + SERVE_UI: "true" + DATABASE_URL: ${DATABASE_URL} + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} + PAPERCLIP_HOME: /paperclip + PAPERCLIP_INSTANCE_ID: default + PAPERCLIP_CONFIG: /paperclip/instances/default/config.json + PAPERCLIP_DEPLOYMENT_MODE: ${PAPERCLIP_DEPLOYMENT_MODE} + PAPERCLIP_DEPLOYMENT_EXPOSURE: ${PAPERCLIP_DEPLOYMENT_EXPOSURE} + PAPERCLIP_PUBLIC_URL: ${PAPERCLIP_PUBLIC_URL} + expose: + - "3100" + volumes: + - paperclip-data:/paperclip + +volumes: + postgres-data: {} + paperclip-data: {} \ No newline at end of file diff --git a/blueprints/paperclip/logo.svg b/blueprints/paperclip/logo.svg new file mode 100644 index 00000000..44a8f649 --- /dev/null +++ b/blueprints/paperclip/logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/blueprints/paperclip/template.toml b/blueprints/paperclip/template.toml new file mode 100644 index 00000000..6a79e07c --- /dev/null +++ b/blueprints/paperclip/template.toml @@ -0,0 +1,20 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +better_auth_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "DATABASE_URL=postgres://paperclip:${postgres_password}@postgres:5432/paperclip", + "BETTER_AUTH_SECRET=${better_auth_secret}", + "PAPERCLIP_DEPLOYMENT_MODE=authenticated", + "PAPERCLIP_DEPLOYMENT_EXPOSURE=private", + "PAPERCLIP_PUBLIC_URL=https://${main_domain}" +] +mounts = [] + +[[config.domains]] +serviceName = "paperclip" +port = 3100 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 86705cb4..a3fdfc39 100644 --- a/meta.json +++ b/meta.json @@ -5129,6 +5129,24 @@ "open-source" ] }, + { + "id": "paperclip", + "name": "Paperclip", + "version": "latest", + "description": "Paperclip is an open-source control plane for managing teams of AI agents, goals, budgets, tickets, and recurring work.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/paperclipai/paperclip", + "website": "https://paperclip.ing/", + "docs": "https://docs.paperclip.ing/" + }, + "tags": [ + "ai", + "agents", + "automation", + "self-hosted" + ] + }, { "id": "parseable", "name": "Parseable", From f14cc64043f84d7bc3e440267ad311336d17d692 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:03:54 +0400 Subject: [PATCH 09/55] feat: add PrivateBin template (#947) * feat: add PrivateBin template * chore: pin privatebin template version --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/privatebin/docker-compose.yml | 11 +++++++++++ blueprints/privatebin/privatebin.svg | 5 +++++ blueprints/privatebin/template.toml | 11 +++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 blueprints/privatebin/docker-compose.yml create mode 100644 blueprints/privatebin/privatebin.svg create mode 100644 blueprints/privatebin/template.toml diff --git a/blueprints/privatebin/docker-compose.yml b/blueprints/privatebin/docker-compose.yml new file mode 100644 index 00000000..dc621ca0 --- /dev/null +++ b/blueprints/privatebin/docker-compose.yml @@ -0,0 +1,11 @@ +services: + privatebin: + image: privatebin/nginx-fpm-alpine:2.0.4 + restart: unless-stopped + volumes: + - privatebin_data:/srv/data + ports: + - "8080" + +volumes: + privatebin_data: diff --git a/blueprints/privatebin/privatebin.svg b/blueprints/privatebin/privatebin.svg new file mode 100644 index 00000000..0391799d --- /dev/null +++ b/blueprints/privatebin/privatebin.svg @@ -0,0 +1,5 @@ + + + + P + diff --git a/blueprints/privatebin/template.toml b/blueprints/privatebin/template.toml new file mode 100644 index 00000000..747c3bc7 --- /dev/null +++ b/blueprints/privatebin/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "privatebin" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index a3fdfc39..6dd27215 100644 --- a/meta.json +++ b/meta.json @@ -5585,6 +5585,23 @@ ], "dokploy_version": "<0.22.5" }, + { + "id": "privatebin", + "name": "PrivateBin", + "version": "2.0.4", + "description": "PrivateBin is a minimalist, open-source online pastebin where the server has zero knowledge of pasted data.", + "logo": "privatebin.svg", + "links": { + "github": "https://github.com/PrivateBin/PrivateBin", + "website": "https://privatebin.info/", + "docs": "https://github.com/PrivateBin/docker-nginx-fpm-alpine" + }, + "tags": [ + "pastebin", + "privacy", + "self-hosted" + ] + }, { "id": "prometheus", "name": "Prometheus", From b0429d94b9d2df2812388a5e3196a6698e8b784a Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:04:12 +0400 Subject: [PATCH 10/55] fix: keep Valkey internal (#937) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/valkey/docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/blueprints/valkey/docker-compose.yml b/blueprints/valkey/docker-compose.yml index bcabc622..f52cfafd 100644 --- a/blueprints/valkey/docker-compose.yml +++ b/blueprints/valkey/docker-compose.yml @@ -4,8 +4,6 @@ services: valkey: image: valkey/valkey:8.1.4 restart: unless-stopped - ports: - - 6379 volumes: - ../files/valkey.conf:/etc/valkey/valkey.conf - valkey-data:/data From ac716a337f3ce1162e048b165fa27d4c97f0c354 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 00:04:26 +0400 Subject: [PATCH 11/55] feat: add Rallly template (#933) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/rallly/docker-compose.yml | 66 ++++++++++++++++++++++++++++ blueprints/rallly/rallly.svg | 8 ++++ blueprints/rallly/template.toml | 40 +++++++++++++++++ meta.json | 17 +++++++ 4 files changed, 131 insertions(+) create mode 100644 blueprints/rallly/docker-compose.yml create mode 100644 blueprints/rallly/rallly.svg create mode 100644 blueprints/rallly/template.toml diff --git a/blueprints/rallly/docker-compose.yml b/blueprints/rallly/docker-compose.yml new file mode 100644 index 00000000..ac6badae --- /dev/null +++ b/blueprints/rallly/docker-compose.yml @@ -0,0 +1,66 @@ +version: "3.8" + +services: + rallly: + image: lukevella/rallly:4.11.1 + restart: unless-stopped + depends_on: + - db + - minio + - minio-init + environment: + NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL} + DATABASE_URL: ${DATABASE_URL} + SECRET_PASSWORD: ${SECRET_PASSWORD} + SUPPORT_EMAIL: ${SUPPORT_EMAIL} + NOREPLY_EMAIL: ${NOREPLY_EMAIL} + SMTP_HOST: ${SMTP_HOST} + SMTP_PORT: ${SMTP_PORT} + SMTP_USER: ${SMTP_USER} + SMTP_PWD: ${SMTP_PWD} + SMTP_SECURE: ${SMTP_SECURE} + S3_ENDPOINT: ${S3_ENDPOINT} + S3_BUCKET_NAME: ${S3_BUCKET_NAME} + S3_REGION: ${S3_REGION} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} + INITIAL_ADMIN_EMAIL: ${INITIAL_ADMIN_EMAIL} + + db: + image: postgres:14-alpine + restart: unless-stopped + environment: + POSTGRES_DB: rallly + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - rallly-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d rallly"] + interval: 10s + timeout: 5s + retries: 5 + + minio: + image: minio/minio:latest + restart: unless-stopped + command: server /data + environment: + MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID} + MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY} + volumes: + - rallly-minio:/data + + minio-init: + image: minio/mc:latest + depends_on: + - minio + restart: "no" + entrypoint: > + /bin/sh -c " + until mc alias set local http://minio:9000 ${S3_ACCESS_KEY_ID} ${S3_SECRET_ACCESS_KEY}; do sleep 2; done; + mc mb --ignore-existing local/rallly; + " + +volumes: + rallly-db: + rallly-minio: \ No newline at end of file diff --git a/blueprints/rallly/rallly.svg b/blueprints/rallly/rallly.svg new file mode 100644 index 00000000..b4e44b30 --- /dev/null +++ b/blueprints/rallly/rallly.svg @@ -0,0 +1,8 @@ + + Rallly + + + + + + \ No newline at end of file diff --git a/blueprints/rallly/template.toml b/blueprints/rallly/template.toml new file mode 100644 index 00000000..902a72f8 --- /dev/null +++ b/blueprints/rallly/template.toml @@ -0,0 +1,40 @@ +[variables] +main_domain = "${domain}" +support_email = "${email}" +noreply_email = "${email}" +initial_admin_email = "${email}" +postgres_password = "${password:32}" +secret_password = "${password:32}" +s3_access_key_id = "rallly" +s3_secret_access_key = "${password:32}" +smtp_host = "smtp.example.com" +smtp_port = "587" +smtp_user = "change-me" +smtp_password = "${password:32}" +smtp_secure = "false" + +[config] +env = [ + "NEXT_PUBLIC_BASE_URL=https://${main_domain}", + "DATABASE_URL=postgres://postgres:${postgres_password}@db:5432/rallly", + "SECRET_PASSWORD=${secret_password}", + "SUPPORT_EMAIL=${support_email}", + "NOREPLY_EMAIL=${noreply_email}", + "SMTP_HOST=${smtp_host}", + "SMTP_PORT=${smtp_port}", + "SMTP_USER=${smtp_user}", + "SMTP_PWD=${smtp_password}", + "SMTP_SECURE=${smtp_secure}", + "S3_ENDPOINT=http://minio:9000", + "S3_BUCKET_NAME=rallly", + "S3_REGION=us-east-1", + "S3_ACCESS_KEY_ID=${s3_access_key_id}", + "S3_SECRET_ACCESS_KEY=${s3_secret_access_key}", + "INITIAL_ADMIN_EMAIL=${initial_admin_email}", + "POSTGRES_PASSWORD=${postgres_password}", +] + +[[config.domains]] +serviceName = "rallly" +port = 3000 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 6dd27215..7b91c0d2 100644 --- a/meta.json +++ b/meta.json @@ -5759,6 +5759,23 @@ "rabbitmq" ] }, + { + "id": "rallly", + "name": "Rallly", + "version": "4.11.1", + "description": "Rallly is an open-source scheduling and collaboration tool for organizing events and meetings.", + "logo": "rallly.svg", + "links": { + "website": "https://rallly.co/", + "docs": "https://support.rallly.co/self-hosting/introduction", + "github": "https://github.com/lukevella/rallly" + }, + "tags": [ + "scheduling", + "polls", + "collaboration" + ] + }, { "id": "reactive-resume", "name": "Reactive Resume", From c9dc13aab86f8a772239619adf297bc83465eb29 Mon Sep 17 00:00:00 2001 From: Luke Dunsmore Date: Tue, 7 Jul 2026 21:09:30 +0100 Subject: [PATCH 12/55] feat: update MediaFetch template version to 1.3.5 (#879) * feat: add MediaFetch template * Corrected .toml * .toml * Updated template to version 1.3.5 * Codex issues raised now fixed * Codex issues addressed * TOML Update --- blueprints/mediafetch/docker-compose.yml | 9 ++++++--- blueprints/mediafetch/template.toml | 14 +++++++++++--- meta.json | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/blueprints/mediafetch/docker-compose.yml b/blueprints/mediafetch/docker-compose.yml index f0c690cd..17ba6176 100644 --- a/blueprints/mediafetch/docker-compose.yml +++ b/blueprints/mediafetch/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: mediafetch: - image: lukedunsmoto/mediafetch:latest + image: lukedunsmoto/mediafetch:1.3.5 restart: unless-stopped expose: - "3002" @@ -11,8 +11,11 @@ services: - PORT=3002 - BASIC_AUTH_USER=${BASIC_AUTH_USER} - BASIC_AUTH_PASS=${BASIC_AUTH_PASS} - - PUBLIC_BASE_URL=https://${DOMAIN} - - OUTPUT_DIR=/data/downloads + - PUBLIC_BASE_URL=${PUBLIC_BASE_URL} + - OUTPUT_DIR=${OUTPUT_DIR} + - MAX_CONCURRENT_JOBS=${MAX_CONCURRENT_JOBS} + - JOB_TIMEOUT_MS=${JOB_TIMEOUT_MS} + - VERSION_CHECK_TTL_MS=${VERSION_CHECK_TTL_MS} volumes: mediafetch_data: \ No newline at end of file diff --git a/blueprints/mediafetch/template.toml b/blueprints/mediafetch/template.toml index 07672d4e..791bd07f 100644 --- a/blueprints/mediafetch/template.toml +++ b/blueprints/mediafetch/template.toml @@ -1,13 +1,21 @@ [variables] +main_domain = "${domain}" +PORT="3002" +PUBLIC_BASE_URL="https://${main_domain}" +OUTPUT_DIR="/data/downloads" BASIC_AUTH_USER = "admin" -BASIC_AUTH_PASS = "${password:12}" +BASIC_AUTH_PASS = "changeme" +MAX_CONCURRENT_JOBS="2" +JOB_TIMEOUT_MS="600000" +VERSION_CHECK_TTL_MS="21600000" [config] + [[config.domains]] name = "Domain" -variable = "DOMAIN" serviceName = "mediafetch" -port = 3002 +port = "3002" +host = "${main_domain}" [[config.mounts]] name = "Downloads" diff --git a/meta.json b/meta.json index 7b91c0d2..55a16f71 100644 --- a/meta.json +++ b/meta.json @@ -4248,7 +4248,7 @@ { "id": "mediafetch", "name": "MediaFetch", - "version": "1.1.1", + "version": "1.3.5", "description": "A tiny, self-hosted web wrapper for yt-dlp to download video and audio. Optional basic auth.", "logo": "mediafetch.svg", "links": { From 0074651a45c20ee7f60fac203e12417cafa8285d Mon Sep 17 00:00:00 2001 From: Artemis-ignis Date: Wed, 8 Jul 2026 05:10:00 +0900 Subject: [PATCH 13/55] feat: add Miniflux template (#881) * feat: add Miniflux template * fix: guard GitHub star count fallback --------- Co-authored-by: Artemis-ignis --- app/src/components/Navigation.tsx | 4 ++- blueprints/miniflux/docker-compose.yml | 45 ++++++++++++++++++++++++++ blueprints/miniflux/miniflux.svg | 1 + blueprints/miniflux/template.toml | 17 ++++++++++ meta.json | 17 ++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 blueprints/miniflux/docker-compose.yml create mode 100644 blueprints/miniflux/miniflux.svg create mode 100644 blueprints/miniflux/template.toml diff --git a/app/src/components/Navigation.tsx b/app/src/components/Navigation.tsx index 47639224..adba7dd7 100644 --- a/app/src/components/Navigation.tsx +++ b/app/src/components/Navigation.tsx @@ -13,7 +13,9 @@ const Navigation = () => { "https://api.github.com/repos/dokploy/dokploy" ); const data = await response.json(); - setGithubStars(data.stargazers_count); + setGithubStars( + Number.isFinite(data.stargazers_count) ? data.stargazers_count : 0 + ); } catch (error) { console.error("Error fetching GitHub stars:", error); } diff --git a/blueprints/miniflux/docker-compose.yml b/blueprints/miniflux/docker-compose.yml new file mode 100644 index 00000000..874021ac --- /dev/null +++ b/blueprints/miniflux/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.8" + +services: + miniflux: + image: miniflux/miniflux:2.2.19 + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + DATABASE_URL: user=miniflux password=${POSTGRES_PASSWORD} dbname=miniflux host=db sslmode=disable + RUN_MIGRATIONS: "1" + CREATE_ADMIN: "1" + ADMIN_USERNAME: ${ADMIN_USERNAME} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + BASE_URL: https://${MINIFLUX_DOMAIN} + expose: + - "8080" + healthcheck: + test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + + db: + image: postgres:18-alpine + restart: unless-stopped + environment: + POSTGRES_USER: miniflux + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: miniflux + expose: + - "5432" + volumes: + - miniflux-db:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U miniflux -d miniflux"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + miniflux-db: diff --git a/blueprints/miniflux/miniflux.svg b/blueprints/miniflux/miniflux.svg new file mode 100644 index 00000000..3537ce7e --- /dev/null +++ b/blueprints/miniflux/miniflux.svg @@ -0,0 +1 @@ + diff --git a/blueprints/miniflux/template.toml b/blueprints/miniflux/template.toml new file mode 100644 index 00000000..d6f553f7 --- /dev/null +++ b/blueprints/miniflux/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +admin_username = "admin" +admin_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "miniflux" +port = 8080 +host = "${main_domain}" + +[config.env] +MINIFLUX_DOMAIN = "${main_domain}" +POSTGRES_PASSWORD = "${postgres_password}" +ADMIN_USERNAME = "${admin_username}" +ADMIN_PASSWORD = "${admin_password}" diff --git a/meta.json b/meta.json index 55a16f71..6584865f 100644 --- a/meta.json +++ b/meta.json @@ -4345,6 +4345,23 @@ "docker" ] }, + { + "id": "miniflux", + "name": "Miniflux", + "version": "2.2.19", + "description": "Miniflux is a minimalist and opinionated feed reader for self-hosted RSS and Atom subscriptions.", + "logo": "miniflux.svg", + "links": { + "github": "https://github.com/miniflux/v2", + "website": "https://miniflux.app/", + "docs": "https://miniflux.app/docs/" + }, + "tags": [ + "rss", + "feed-reader", + "news" + ] + }, { "id": "minio", "name": "Minio", From 201fcd1ed8acd99b68374e7b620bfcca3630a26f Mon Sep 17 00:00:00 2001 From: Robert Da Silva Date: Tue, 7 Jul 2026 22:11:16 +0200 Subject: [PATCH 14/55] feat(blueprint): add LimeSurvey template (#878) --- blueprints/limesurvey/docker-compose.yml | 40 ++++++++++++++++++++++++ blueprints/limesurvey/limesurvey.svg | 38 ++++++++++++++++++++++ blueprints/limesurvey/template.toml | 19 +++++++++++ meta.json | 17 ++++++++++ 4 files changed, 114 insertions(+) create mode 100644 blueprints/limesurvey/docker-compose.yml create mode 100644 blueprints/limesurvey/limesurvey.svg create mode 100644 blueprints/limesurvey/template.toml diff --git a/blueprints/limesurvey/docker-compose.yml b/blueprints/limesurvey/docker-compose.yml new file mode 100644 index 00000000..936e5199 --- /dev/null +++ b/blueprints/limesurvey/docker-compose.yml @@ -0,0 +1,40 @@ +version: "3.8" + +services: + db: + image: mariadb:10.11 + restart: always + volumes: + - db_data:/var/lib/mysql + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=limesurvey + - MYSQL_USER=limesurvey + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + + limesurvey: + image: martialblog/limesurvey:latest + restart: always + volumes: + - limesurvey_upload:/var/www/html/upload + environment: + - ADMIN_EMAIL=${ADMIN_EMAIL} + - ADMIN_NAME=Admin + - ADMIN_USER=admin + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - DB_HOST=db + - DB_MYSQL_ENGINE=InnoDB + - DB_NAME=limesurvey + - DB_PASSWORD=${MYSQL_PASSWORD} + - DB_USERNAME=limesurvey + - DEBUG=0 + - DEBUG_SQL=0 + - PUBLIC_URL=${PUBLIC_URL} + - SHOW_SCRIPT_NAME=false + - URL_FORMAT=path + depends_on: + - db + +volumes: + db_data: + limesurvey_upload: diff --git a/blueprints/limesurvey/limesurvey.svg b/blueprints/limesurvey/limesurvey.svg new file mode 100644 index 00000000..9ee4c0c6 --- /dev/null +++ b/blueprints/limesurvey/limesurvey.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/blueprints/limesurvey/template.toml b/blueprints/limesurvey/template.toml new file mode 100644 index 00000000..07623f50 --- /dev/null +++ b/blueprints/limesurvey/template.toml @@ -0,0 +1,19 @@ +[variables] +main_domain = "${domain}" +mysql_root_password = "${password:32}" +mysql_user_password = "${password:32}" +admin_email = "${email}" +admin_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "limesurvey" +port = 8080 +host = "${main_domain}" + +[config.env] +PUBLIC_URL = "http://${main_domain}" +ADMIN_EMAIL = "${admin_email}" +ADMIN_PASSWORD = "${admin_password}" +MYSQL_PASSWORD = "${mysql_user_password}" +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" diff --git a/meta.json b/meta.json index 6584865f..65dde0c7 100644 --- a/meta.json +++ b/meta.json @@ -3882,6 +3882,23 @@ "language" ] }, + { + "id": "limesurvey", + "name": "LimeSurvey", + "version": "6.17.2+260507", + "description": "LimeSurvey is a powerful, open-source survey platform, making it simple to create online surveys and forms with unmatched flexibility.", + "logo": "limesurvey.svg", + "links": { + "github": "https://github.com/LimeSurvey/LimeSurvey", + "website": "https://www.limesurvey.org", + "docs": "https://www.limesurvey.org/manual/" + }, + "tags": [ + "forms", + "questionnaire", + "survey" + ] + }, { "id": "linkding", "name": "Linkding", From cc99a025d3bb998720e44aa5b12b57d7327f5670 Mon Sep 17 00:00:00 2001 From: atlasclaw369 Date: Wed, 8 Jul 2026 00:12:07 +0400 Subject: [PATCH 15/55] feat: add Homepage template (#871) Co-authored-by: atlasclaw369 <268303653+atlasclaw369@users.noreply.github.com> --- blueprints/homepage/docker-compose.yml | 9 +++++++++ blueprints/homepage/homepage.png | Bin 0 -> 1407 bytes blueprints/homepage/template.toml | 13 +++++++++++++ meta.json | 18 ++++++++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 blueprints/homepage/docker-compose.yml create mode 100644 blueprints/homepage/homepage.png create mode 100644 blueprints/homepage/template.toml diff --git a/blueprints/homepage/docker-compose.yml b/blueprints/homepage/docker-compose.yml new file mode 100644 index 00000000..2a6d48b9 --- /dev/null +++ b/blueprints/homepage/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.8" +services: + homepage: + image: ghcr.io/gethomepage/homepage:v1.13.1 + restart: unless-stopped + expose: + - 3000 + volumes: + - ../files/homepage-config:/app/config diff --git a/blueprints/homepage/homepage.png b/blueprints/homepage/homepage.png new file mode 100644 index 0000000000000000000000000000000000000000..0a11f3dc8a690fe9fb31f6cd695f27e7bf53e5e6 GIT binary patch literal 1407 zcmZ8hX;4!K5RS$c5rk2&j6fM$w1PlT6VW`5gd4&kgcKnhiXt%r29scjlzS?oawr7k zcrk^fwNB+Iks%yG3j!8=!V^aWm{ceU!3G(@4PVHVeAdp#JAOE)VtwYl*+pK%^-aiiuXZ`QX_ts7Csh%_~9sz!uyZU5_Ij|Vj zuDA!U+=Vm+k5yq06p7-ub>r`b-{{b}gObHG8j!1G7 z(K^Q|Xm!&vo;dYp&%=2ih^^%7PlYHa8v4dbEq(&$>n;BkT>3v+hEet*DK+^6PWbcT zqSa;Hv;8t`JGYH1(^9pf`H*Mrw+#lv-TjJiX`Kb~SZSU1oS%**e_!ICOc02o8RkHti? zm7-!ujDz>qNmY_18MLr@r}ipP6{gPP>rO3Cgb$8!3T}$CX;fRaaXVcLb>Loby}uGQ zKvopnWp93jXG4_`Nmx5lP&3#9ynu*}AVOe9Mk%(<|DY4+twc22y9unxUtL=Fo zdm2)lK-Y{o+odFYEBr{hYf4m}kM`L4m)*l7uI^F>t_Ysj8LxeUJMikp=N|b1s^2o( z*uKuAG3F%2B;ImsY?3d-zT7E(h6z=aYWnjk2|c;;^W@Q8$uld>ULP|mp#>$!lCBdD zo>Lr$f~~t?D6={ASm7+o*b@Rsx*35zVz1{?yqm-Z*|uTBCjx) z2y8jN2A2CgsrOYSxrgNO0|v2fnmH36b-|@gQTi0{yaIbrBk%K38|QDB$ceMa?%wy` zQs|f6ys^*w#reU#kwEA`pJ376BaE&};M`*}gR}%DbY9_v$Q)wd*a|)yhz@gIQ z^mbhd?=ocMmSv=woXvQ5TJ*-?k}Q0;xMVR}!dsIk=G?~Aw-W5cVqHN;6I)_`qB(|c z@W~8OKXS{bk!SK0AV*48dUe5Tx{4L}<>^Eyo4&C=qU5)`hT2qOU9vcsmA-h1y*n)$ znW`f1XqUJd8v*bdR0~Y+<6R;HxN)RV+UU^y6y6;^DVb-g%t`GA&`iR3d^ns63>f%u zI_N{BzLrocd&JYb*(OcAm8|mj@!>J_r~8*IJe`xHPkJ_s|99&x&L3S1w{W+bQqfdA zfPA!*S19ppaw^9$o5@J0XP$FpbFOuMYy3fv{M(was2i4*zm-#{jkY+QK*1~K`q Date: Wed, 8 Jul 2026 04:23:01 +0800 Subject: [PATCH 16/55] Add qui A fast, modern web interface for qBittorrent. Manage multiple qBittorrent instances from a single, lightweight application built by autobrr (#825) * feat: add qui template qui is a fast, modern web interface for qBittorrent by autobrr, allowing management of multiple qBittorrent instances from a single, lightweight self-hosted application. - docker-compose.yml: pinned ghcr.io/autobrr/qui:1.17.0, exposes 7476 internally (no host port; Dokploy's Traefik handles routing), persistent named volume for /config. - template.toml: maps the qui service port 7476 to the user-supplied domain so Dokploy's Traefik can proxy it. - meta.json: register the template with logo and tags. * fix(qui): use latest image tag ghcr.io/autobrr/qui has no v1.17.0 tag published; only 'latest' and PR/dev tags are available. Switch the image and meta.json version to 'latest' so the template actually deploys. * revert: restore duplicate strapi entry removed by dedupe script The previous commit accidentally removed a pre-existing duplicate strapi entry as a side effect of running process-meta. That entry exists upstream and is unrelated to the qui template; restore it verbatim. * fix: deduplicate meta.json to pass CI validation The upstream meta.json had a pre-existing duplicate strapi entry. Remove it so dedupe-and-sort produces identical output to the committed file, satisfying the validate-meta CI check. * fix(qui): replace logo with autobrr org avatar (128x128) --- blueprints/qui/docker-compose.yml | 13 +++++++++++++ blueprints/qui/qui.png | Bin 0 -> 12411 bytes blueprints/qui/template.toml | 9 +++++++++ meta.json | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 blueprints/qui/docker-compose.yml create mode 100644 blueprints/qui/qui.png create mode 100644 blueprints/qui/template.toml diff --git a/blueprints/qui/docker-compose.yml b/blueprints/qui/docker-compose.yml new file mode 100644 index 00000000..0158d9cd --- /dev/null +++ b/blueprints/qui/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + qui: + image: ghcr.io/autobrr/qui:latest + restart: unless-stopped + expose: + - "7476" + volumes: + - qui-config:/config + +volumes: + qui-config: diff --git a/blueprints/qui/qui.png b/blueprints/qui/qui.png new file mode 100644 index 0000000000000000000000000000000000000000..64897dc4516cca619f265498c6305030b00806bf GIT binary patch literal 12411 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 literal 0 HcmV?d00001 diff --git a/blueprints/qui/template.toml b/blueprints/qui/template.toml new file mode 100644 index 00000000..3858b997 --- /dev/null +++ b/blueprints/qui/template.toml @@ -0,0 +1,9 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "qui" +port = 7476 +host = "${main_domain}" +path = "/" diff --git a/meta.json b/meta.json index b63a3177..af267b5d 100644 --- a/meta.json +++ b/meta.json @@ -5794,6 +5794,25 @@ "analytics" ] }, + { + "id": "qui", + "name": "qui", + "version": "latest", + "description": "A fast, modern web interface for qBittorrent. Manage multiple qBittorrent instances from a single, lightweight application built by autobrr.", + "logo": "qui.png", + "links": { + "github": "https://github.com/autobrr/qui", + "website": "https://getqui.com", + "docs": "https://getqui.com" + }, + "tags": [ + "torrent", + "qbittorrent", + "download", + "media", + "self-hosted" + ] + }, { "id": "rabbitmq", "name": "RabbitMQ", From f1210e6ac4feda626eab3cf45097e00c35c44741 Mon Sep 17 00:00:00 2001 From: Ennio Marke Date: Tue, 7 Jul 2026 22:34:26 +0200 Subject: [PATCH 17/55] feat: Add Seanime and remove strapai duplicate entry (#808) * v1.0.0 * v1.0.1 * Update blueprints/seanime/docker-compose.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update blueprints/seanime/docker-compose.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update blueprints/seanime/docker-compose.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * v1.0.2 * Update blueprints/seanime/docker-compose.yml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * v1.0.4 * v1.0.5 * v1.0.6 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- blueprints/seanime/docker-compose.yml | 26 ++++++++++++++++++++++++++ blueprints/seanime/seanime.png | Bin 0 -> 20568 bytes blueprints/seanime/template.toml | 7 +++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 blueprints/seanime/docker-compose.yml create mode 100644 blueprints/seanime/seanime.png create mode 100644 blueprints/seanime/template.toml diff --git a/blueprints/seanime/docker-compose.yml b/blueprints/seanime/docker-compose.yml new file mode 100644 index 00000000..9f6992a0 --- /dev/null +++ b/blueprints/seanime/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.8" +services: + seanime-init: + image: busybox:1.37.0 + user: "0:0" + volumes: + - ./seanime-config:/mnt/config + - ./anime:/mnt/anime + - ./downloads:/mnt/downloads + command: + [ "sh", "-c", "chown -R 1000:1000 /mnt/config && find /mnt/config -type d -exec chmod 755 {} + && find /mnt/config -type f -exec chmod 644 {} +",] + + seanime: + image: umagistr/seanime:v3.6.1-rootless + depends_on: + seanime-init: + condition: service_completed_successfully + environment: + - SEANIME_SERVER_HOST=0.0.0.0 + expose: + - "43211" + volumes: + - ./seanime-config:/home/seanime/.config/Seanime + - ./anime:/anime + - ./downloads:/downloads + restart: unless-stopped \ No newline at end of file diff --git a/blueprints/seanime/seanime.png b/blueprints/seanime/seanime.png new file mode 100644 index 0000000000000000000000000000000000000000..9e2459d918bba4eba1df66bc25e69af30581dc67 GIT binary patch literal 20568 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 literal 0 HcmV?d00001 diff --git a/blueprints/seanime/template.toml b/blueprints/seanime/template.toml new file mode 100644 index 00000000..eabd94ef --- /dev/null +++ b/blueprints/seanime/template.toml @@ -0,0 +1,7 @@ +[variables] +main_domain = "${domain}" + +[[config.domains]] +serviceName = "seanime" +port = 43211 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index af267b5d..1cd02407 100644 --- a/meta.json +++ b/meta.json @@ -6103,6 +6103,23 @@ "storage" ] }, + { + "id": "seanime", + "name": "Seanime", + "version": "3.6.1-rootless", + "description": "A self-hosted anime streaming platform.", + "logo": "seanime.png", + "links": { + "github": "https://github.com/5rahim/seanime", + "website": "https://seanime.app/", + "docs": "https://seanime.app/docs" + }, + "tags": [ + "media", + "anime", + "streaming" + ] + }, { "id": "searxng", "name": "SearXNG", From bc31807d6f2a7f433a26985e4e426604ca79fe87 Mon Sep 17 00:00:00 2001 From: Stefan Ernst Date: Tue, 7 Jul 2026 23:02:48 +0200 Subject: [PATCH 18/55] feat: add windshift to dokploy (#787) * Adding Windshift for Dokploy * Added up-to-date version and better tags * Updated logo with less transparency * Update docker-compose.yml with latest version Signed-off-by: Stefan Ernst --------- Signed-off-by: Stefan Ernst --- blueprints/windshift/docker-compose.yml | 44 ++++++++++++++++++++++++ blueprints/windshift/template.toml | 19 ++++++++++ blueprints/windshift/windshift.png | Bin 0 -> 81733 bytes meta.json | 18 ++++++++++ 4 files changed, 81 insertions(+) create mode 100644 blueprints/windshift/docker-compose.yml create mode 100644 blueprints/windshift/template.toml create mode 100644 blueprints/windshift/windshift.png diff --git a/blueprints/windshift/docker-compose.yml b/blueprints/windshift/docker-compose.yml new file mode 100644 index 00000000..81008518 --- /dev/null +++ b/blueprints/windshift/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + windshift: + image: ghcr.io/windshiftapp/windshift:v0.5.3 + restart: unless-stopped + environment: + - BASE_URL=https://${DOMAIN} + - PORT=8080 + - USE_PROXY=true + - SSO_SECRET=${SSO_SECRET} + - ATTACHMENT_PATH=/data/attachments + - DB_TYPE=postgres + - POSTGRES_HOST=db + - POSTGRES_PORT=5432 + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + - LOG_LEVEL=info + - LOG_FORMAT=json + volumes: + - windshift-data:/data + depends_on: + db: + condition: service_healthy + + db: + image: postgres:17.4 + restart: unless-stopped + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + windshift-data: {} + postgres-data: {} diff --git a/blueprints/windshift/template.toml b/blueprints/windshift/template.toml new file mode 100644 index 00000000..e1e4cb02 --- /dev/null +++ b/blueprints/windshift/template.toml @@ -0,0 +1,19 @@ +[variables] +main_domain = "${domain}" +sso_secret = "${hash:64}" +db_password = "${password:32}" + +[config] +env = [ + "DOMAIN=${main_domain}", + "SSO_SECRET=${sso_secret}", + "POSTGRES_USER=windshift", + "POSTGRES_PASSWORD=${db_password}", + "POSTGRES_DB=windshift", +] +mounts = [] + +[[config.domains]] +serviceName = "windshift" +port = 8080 +host = "${main_domain}" diff --git a/blueprints/windshift/windshift.png b/blueprints/windshift/windshift.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef1bee6cf498604a05a4f1b23c58379424f2552 GIT binary patch literal 81733 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 T9QuCD00000NkvXXu0mjfo{nUn literal 0 HcmV?d00001 diff --git a/meta.json b/meta.json index 1cd02407..7c953c6a 100644 --- a/meta.json +++ b/meta.json @@ -7109,6 +7109,24 @@ "os" ] }, + { + "id": "windshift", + "name": "Windshift", + "version": "v0.5.0", + "description": "Self-hosted work management platform with projects, tasks, sprints, and team collaboration.", + "logo": "windshift.png", + "links": { + "github": "https://github.com/Windshiftapp/core", + "website": "https://windshift.sh/", + "docs": "https://windshift.sh/docs" + }, + "tags": [ + "project-management", + "collaboration", + "productivity", + "self-hosted" + ] + }, { "id": "wordpress", "name": "Wordpress", From 22f1fe5ee6a5874d2c125787d201ed6377b953d8 Mon Sep 17 00:00:00 2001 From: Benjamin Nussbaum Date: Tue, 7 Jul 2026 23:02:58 +0200 Subject: [PATCH 19/55] feat(blueprint): update name of trmnl-byos-laravel and version (#789) --- .../docker-compose.yml | 4 ++-- .../larapaper.svg} | 0 .../template.toml | 2 +- meta.json | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) rename blueprints/{trmnl-byos-laravel => trmnl-larapaper}/docker-compose.yml (88%) rename blueprints/{trmnl-byos-laravel/byos-laravel.svg => trmnl-larapaper/larapaper.svg} (100%) rename blueprints/{trmnl-byos-laravel => trmnl-larapaper}/template.toml (87%) diff --git a/blueprints/trmnl-byos-laravel/docker-compose.yml b/blueprints/trmnl-larapaper/docker-compose.yml similarity index 88% rename from blueprints/trmnl-byos-laravel/docker-compose.yml rename to blueprints/trmnl-larapaper/docker-compose.yml index 61674d23..ffd54306 100644 --- a/blueprints/trmnl-byos-laravel/docker-compose.yml +++ b/blueprints/trmnl-larapaper/docker-compose.yml @@ -1,6 +1,6 @@ services: - trmnl-byos-laravel: - image: ghcr.io/usetrmnl/byos_laravel:0.27.0 + trmnl-larapaper: + image: ghcr.io/usetrmnl/larapaper:0.32.1 environment: - APP_URL=${APP_URL} - PHP_OPCACHE_ENABLE=${PHP_OPCACHE_ENABLE} diff --git a/blueprints/trmnl-byos-laravel/byos-laravel.svg b/blueprints/trmnl-larapaper/larapaper.svg similarity index 100% rename from blueprints/trmnl-byos-laravel/byos-laravel.svg rename to blueprints/trmnl-larapaper/larapaper.svg diff --git a/blueprints/trmnl-byos-laravel/template.toml b/blueprints/trmnl-larapaper/template.toml similarity index 87% rename from blueprints/trmnl-byos-laravel/template.toml rename to blueprints/trmnl-larapaper/template.toml index 6c55be9b..1ce919c1 100644 --- a/blueprints/trmnl-byos-laravel/template.toml +++ b/blueprints/trmnl-larapaper/template.toml @@ -5,7 +5,7 @@ main_domain = "${domain}" mounts = [] [[config.domains]] -serviceName = "trmnl-byos-laravel" +serviceName = "trmnl-larapaper" port = 8080 host = "${main_domain}" diff --git a/meta.json b/meta.json index 7c953c6a..7eb91a2c 100644 --- a/meta.json +++ b/meta.json @@ -6702,15 +6702,15 @@ ] }, { - "id": "trmnl-byos-laravel", - "name": "TRMNL BYOS Laravel", - "version": "0.27.0", - "description": "TRMNL BYOS Laravel is a self-hosted application to manage TRMNL e-ink devices.", - "logo": "byos-laravel.svg", + "id": "trmnl-larapaper", + "name": "TRMNL LaraPaper", + "version": "0.32.1", + "description": "LaraPaper is a self-hosted application (BYOS) to manage TRMNL e-ink devices.", + "logo": "larapaper.svg", "links": { - "github": "https://github.com/usetrmnl/byos_laravel", + "github": "https://github.com/usetrmnl/larapaper", "website": "https://docs.usetrmnl.com/go/diy/byos", - "docs": "https://github.com/usetrmnl/byos_laravel/blob/main/README.md" + "docs": "https://github.com/usetrmnl/larapaper/blob/main/README.md" }, "tags": [ "e-ink" From 989bf839b6214e06cb3400c3d6ede6b623c2fea6 Mon Sep 17 00:00:00 2001 From: Luis <71575458+luis030821@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:03:09 -0500 Subject: [PATCH 20/55] feat: add Agent Zero autonomous AI agent blueprint (#791) * feat: add Agent Zero autonomous AI agent blueprint Added Agent Zero blueprint with SVG logo, Docker Compose configuration for v1.9, and template for deployment. Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities. * feat(meta): add documentation link to agent-zero metadata Added docs URL to the links section in meta.json to provide direct access to the project documentation at https://www.agent-zero.ai/p/docs/ * fix: add length parameter to password helper --- blueprints/agent-zero/agent-zero.svg | 1 + blueprints/agent-zero/docker-compose.yml | 14 ++++++++++++++ blueprints/agent-zero/template.toml | 16 ++++++++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 blueprints/agent-zero/agent-zero.svg create mode 100644 blueprints/agent-zero/docker-compose.yml create mode 100644 blueprints/agent-zero/template.toml diff --git a/blueprints/agent-zero/agent-zero.svg b/blueprints/agent-zero/agent-zero.svg new file mode 100644 index 00000000..1d2aea94 --- /dev/null +++ b/blueprints/agent-zero/agent-zero.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/agent-zero/docker-compose.yml b/blueprints/agent-zero/docker-compose.yml new file mode 100644 index 00000000..fceed3cf --- /dev/null +++ b/blueprints/agent-zero/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.8" +services: + agent-zero: + image: agent0ai/agent-zero:v1.9 + restart: unless-stopped + expose: + - "80" + environment: + - AUTH_LOGIN=${AUTH_LOGIN} + - AUTH_PASSWORD=${AUTH_PASSWORD} + volumes: + - agent-zero-data:/a0/usr +volumes: + agent-zero-data: \ No newline at end of file diff --git a/blueprints/agent-zero/template.toml b/blueprints/agent-zero/template.toml new file mode 100644 index 00000000..bb84517b --- /dev/null +++ b/blueprints/agent-zero/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +auth_login = "${username}" +auth_password = "${password:32}" + +[config] +env = [ + "AUTH_LOGIN=${auth_login}", + "AUTH_PASSWORD=${auth_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "agent-zero" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 7eb91a2c..23dff7cb 100644 --- a/meta.json +++ b/meta.json @@ -122,6 +122,23 @@ "project-management" ] }, + { + "id": "agent-zero", + "name": "Agent Zero", + "version": "v1.9", + "description": "Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities.", + "logo": "agent-zero.svg", + "links": { + "github": "https://github.com/agent0ai/agent-zero", + "website": "https://www.agent-zero.ai", + "docs": "https://www.agent-zero.ai/p/docs/" + }, + "tags": [ + "ai", + "agent", + "automation" + ] + }, { "id": "agentdvr", "name": "Agent DVR", From 091ea7afdaa72a8548e22f69f014628d9fb79c17 Mon Sep 17 00:00:00 2001 From: Vitaliy Kukharik <37010174+vitabaks@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:42:13 +0200 Subject: [PATCH 21/55] Update Autobase to version 2.7 (#830) * Update Autobase to version 2.7 Update autobase console image tag in blueprints/autobase/docker-compose.yml from 2.5.2 to 2.7.2 and bump the corresponding package version in meta.json to 2.7.2 to keep metadata in sync with the deployed image. * Add dbdesk_studio_data Docker volume --------- Co-authored-by: Mauricio Siu --- blueprints/autobase/docker-compose.yml | 4 +++- meta.json | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/blueprints/autobase/docker-compose.yml b/blueprints/autobase/docker-compose.yml index 13b23d57..7582c9c6 100644 --- a/blueprints/autobase/docker-compose.yml +++ b/blueprints/autobase/docker-compose.yml @@ -1,6 +1,6 @@ services: autobase-console: - image: autobase/console:2.5.2 + image: autobase/console:2.7.2 restart: unless-stopped ports: - "80" @@ -10,8 +10,10 @@ services: - PG_CONSOLE_AUTHORIZATION_TOKEN=${PG_CONSOLE_AUTHORIZATION_TOKEN} volumes: - console_postgres:/var/lib/postgresql + - dbdesk_studio_data:/opt/dbdesk-studio - /var/run/docker.sock:/var/run/docker.sock - /tmp/ansible:/tmp/ansible volumes: console_postgres: + dbdesk_studio_data: diff --git a/meta.json b/meta.json index 23dff7cb..e94efea2 100644 --- a/meta.json +++ b/meta.json @@ -561,8 +561,8 @@ { "id": "autobase", "name": "Autobase", - "version": "2.5.2", - "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", + "version": "2.7.2", + "description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).", "links": { "github": "https://github.com/vitabaks/autobase", "website": "https://autobase.tech/", From 1e99d46afe04d6856e402cc5c96ab3e14ea4845a Mon Sep 17 00:00:00 2001 From: Juan Vargas <108702207+JuansesDev@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:42:47 -0500 Subject: [PATCH 22/55] feat: add SendDock template (#821) * feat(senddock): add SendDock template SendDock is an open-source, self-hosted email marketing platform with BYO SMTP. The template ships postgres + redis + the canonical ghcr.io/arkhe-systems/senddock image, with healthchecks, named volumes and the env vars required for a working install. The senddock service runs migrations on startup via goose and serves the Vue dashboard on port 8080. PUBLIC_URL is wired to the Dokploy domain helper so unsubscribe links and broadcasts work out of the box. License key is left empty by default, which gives users the free Core experience; setting SENDDOCK_LICENSE_KEY unlocks the Pro features. Running dedupe-and-sort-meta.js per CONTRIBUTING removed an unrelated duplicate strapi entry that already existed in the index (same version, same logo, listed twice). The senddock entry is the intended addition. * fix(senddock): apply review fixes and bump to 0.6.4.1 - Add required `version: "3.8"` declaration to docker-compose.yml per AGENTS.md - Convert template.toml env from sub-table to array-of-strings syntax - Bump image and meta.json version to 0.6.4.1 (latest stable, fixes Redis rate-limiter restart loop affecting 0.4.x-0.6.4) * fix(senddock): correct website and docs URLs to senddock.dev * fix(senddock): bump to 0.6.5.1 --------- Co-authored-by: Sebastian Vargas Co-authored-by: Mauricio Siu --- blueprints/senddock/docker-compose.yml | 57 ++++++++++++++++++++++++++ blueprints/senddock/senddock.svg | 16 ++++++++ blueprints/senddock/template.toml | 17 ++++++++ meta.json | 18 ++++++++ 4 files changed, 108 insertions(+) create mode 100644 blueprints/senddock/docker-compose.yml create mode 100644 blueprints/senddock/senddock.svg create mode 100644 blueprints/senddock/template.toml diff --git a/blueprints/senddock/docker-compose.yml b/blueprints/senddock/docker-compose.yml new file mode 100644 index 00000000..c4ff03d0 --- /dev/null +++ b/blueprints/senddock/docker-compose.yml @@ -0,0 +1,57 @@ +version: "3.8" +services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=senddock + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=senddock + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U senddock"] + interval: 10s + timeout: 5s + retries: 6 + + redis: + image: redis:7-alpine + restart: unless-stopped + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + + senddock: + image: ghcr.io/arkhe-systems/senddock:0.6.5.1 + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + environment: + - DATABASE_URL=postgres://senddock:${POSTGRES_PASSWORD}@postgres:5432/senddock?sslmode=disable + - REDIS_URL=redis://redis:6379 + - JWT_SECRET=${JWT_SECRET} + - PUBLIC_URL=${PUBLIC_URL} + - FRONTEND_URL=${PUBLIC_URL} + - DEPLOYMENT_MODE=self-hosted + - SENDDOCK_LICENSE_KEY=${SENDDOCK_LICENSE_KEY} + - PORT=8080 + expose: + - 8080 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s + +volumes: + pgdata: + redisdata: diff --git a/blueprints/senddock/senddock.svg b/blueprints/senddock/senddock.svg new file mode 100644 index 00000000..9dc916eb --- /dev/null +++ b/blueprints/senddock/senddock.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/blueprints/senddock/template.toml b/blueprints/senddock/template.toml new file mode 100644 index 00000000..4a8c456e --- /dev/null +++ b/blueprints/senddock/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "JWT_SECRET=${jwt_secret}", + "PUBLIC_URL=https://${main_domain}", + "SENDDOCK_LICENSE_KEY=" +] + +[[config.domains]] +serviceName = "senddock" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index e94efea2..3f70aea8 100644 --- a/meta.json +++ b/meta.json @@ -6175,6 +6175,24 @@ "file-system" ] }, + { + "id": "senddock", + "name": "SendDock", + "version": "0.6.5.1", + "description": "Open-source, self-hosted email marketing platform with BYO SMTP. Manage subscribers, templates, broadcasts, and scheduled campaigns from one place.", + "logo": "senddock.svg", + "links": { + "github": "https://github.com/Arkhe-Systems/senddock", + "website": "https://senddock.dev", + "docs": "https://docs.senddock.dev" + }, + "tags": [ + "email", + "newsletter", + "mailing-list", + "marketing" + ] + }, { "id": "sftpgo", "name": "SFTPGo", From 8a528251674b96e00dc2aeee90ef012ee6efb86e Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:43:07 +0200 Subject: [PATCH 23/55] feat: add Joomla template (#891) * feat: add Joomla template * docs: clarify Joomla admin credential fields --------- Co-authored-by: Mauricio Siu --- blueprints/joomla/docker-compose.yml | 44 ++++++++++++++++++++++++ blueprints/joomla/joomla.svg | 5 +++ blueprints/joomla/template.toml | 51 ++++++++++++++++++++++++++++ meta.json | 18 ++++++++++ 4 files changed, 118 insertions(+) create mode 100644 blueprints/joomla/docker-compose.yml create mode 100644 blueprints/joomla/joomla.svg create mode 100644 blueprints/joomla/template.toml diff --git a/blueprints/joomla/docker-compose.yml b/blueprints/joomla/docker-compose.yml new file mode 100644 index 00000000..e83a420b --- /dev/null +++ b/blueprints/joomla/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + joomla: + image: joomla:6.1.0-apache + restart: unless-stopped + depends_on: + joomla-db: + condition: service_healthy + expose: + - "80" + environment: + JOOMLA_DB_HOST: joomla-db + JOOMLA_DB_USER: ${JOOMLA_DB_USER} + JOOMLA_DB_PASSWORD: ${JOOMLA_DB_PASSWORD} + JOOMLA_DB_NAME: ${JOOMLA_DB_NAME} + JOOMLA_SITE_NAME: ${JOOMLA_SITE_NAME} + JOOMLA_ADMIN_USER: ${JOOMLA_ADMIN_USER} + JOOMLA_ADMIN_USERNAME: ${JOOMLA_ADMIN_USERNAME} + JOOMLA_ADMIN_PASSWORD: ${JOOMLA_ADMIN_PASSWORD} + JOOMLA_ADMIN_EMAIL: ${JOOMLA_ADMIN_EMAIL} + volumes: + - joomla-data:/var/www/html + + joomla-db: + image: mariadb:11.4 + restart: unless-stopped + environment: + MARIADB_DATABASE: ${JOOMLA_DB_NAME} + MARIADB_USER: ${JOOMLA_DB_USER} + MARIADB_PASSWORD: ${JOOMLA_DB_PASSWORD} + MARIADB_RANDOM_ROOT_PASSWORD: "1" + volumes: + - joomla-db:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + joomla-data: + joomla-db: diff --git a/blueprints/joomla/joomla.svg b/blueprints/joomla/joomla.svg new file mode 100644 index 00000000..27239ba8 --- /dev/null +++ b/blueprints/joomla/joomla.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/blueprints/joomla/template.toml b/blueprints/joomla/template.toml new file mode 100644 index 00000000..4f360d66 --- /dev/null +++ b/blueprints/joomla/template.toml @@ -0,0 +1,51 @@ +[variables] +main_domain = "${domain}" +site_name = "Joomla" +admin_name = "Joomla Administrator" +admin_username = "${username}" +admin_password = "${password:24}" +admin_email = "${email}" +db_user = "joomla" +db_password = "${password:32}" +db_name = "joomla" + +[[config.domains]] +serviceName = "joomla" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Joomla + +This template deploys Joomla CMS with MariaDB using the official Joomla Apache image. + +## Access + +- URL: `http://${main_domain}` +- Administrator username: the configured `admin_username` value +- Administrator password: the generated `admin_password` value +- Administrator email: the configured `admin_email` value + +The template uses Joomla's auto-deployment environment variables so the site and administrator account are initialized on first start. + +## Storage + +The template creates persistent volumes for: + +- Joomla application files at `/var/www/html` +- MariaDB data at `/var/lib/mysql` + +Back up both volumes together before upgrades or migrations. +""" + +[config.env] +JOOMLA_SITE_NAME = "${site_name}" +JOOMLA_ADMIN_USER = "${admin_name}" +JOOMLA_ADMIN_USERNAME = "${admin_username}" +JOOMLA_ADMIN_PASSWORD = "${admin_password}" +JOOMLA_ADMIN_EMAIL = "${admin_email}" +JOOMLA_DB_USER = "${db_user}" +JOOMLA_DB_PASSWORD = "${db_password}" +JOOMLA_DB_NAME = "${db_name}" diff --git a/meta.json b/meta.json index 3f70aea8..431878e4 100644 --- a/meta.json +++ b/meta.json @@ -3588,6 +3588,24 @@ "open-source" ] }, + { + "id": "joomla", + "name": "Joomla", + "version": "6.1.0-apache", + "description": "Joomla is a flexible open-source content management system for building websites, portals, and online applications.", + "logo": "joomla.svg", + "links": { + "github": "https://github.com/joomla/joomla-cms", + "website": "https://www.joomla.org/", + "docs": "https://manual.joomla.org/" + }, + "tags": [ + "cms", + "website", + "php", + "mariadb" + ] + }, { "id": "joplin-server", "name": "Joplin Server", From 3ebc2f850f621e7d61e8d2a05d7a9561be31be24 Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:43:15 +0200 Subject: [PATCH 24/55] feat: add TimescaleDB template (#892) --- blueprints/timescaledb/docker-compose.yml | 25 ++++++++++++++ blueprints/timescaledb/template.toml | 40 +++++++++++++++++++++++ blueprints/timescaledb/timescaledb.svg | 10 ++++++ meta.json | 19 +++++++++++ 4 files changed, 94 insertions(+) create mode 100644 blueprints/timescaledb/docker-compose.yml create mode 100644 blueprints/timescaledb/template.toml create mode 100644 blueprints/timescaledb/timescaledb.svg diff --git a/blueprints/timescaledb/docker-compose.yml b/blueprints/timescaledb/docker-compose.yml new file mode 100644 index 00000000..bd136801 --- /dev/null +++ b/blueprints/timescaledb/docker-compose.yml @@ -0,0 +1,25 @@ +version: "3.8" + +services: + timescaledb: + image: timescale/timescaledb:2.26.4-pg17 + restart: unless-stopped + expose: + - 5432 + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + TIMESCALEDB_TELEMETRY: ${TIMESCALEDB_TELEMETRY} + volumes: + - timescaledb-data:/var/lib/postgresql/data + - ../files/001_create_timescaledb.sql:/docker-entrypoint-initdb.d/001_create_timescaledb.sql:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + +volumes: + timescaledb-data: diff --git a/blueprints/timescaledb/template.toml b/blueprints/timescaledb/template.toml new file mode 100644 index 00000000..7ebe8fc3 --- /dev/null +++ b/blueprints/timescaledb/template.toml @@ -0,0 +1,40 @@ +[variables] +postgres_user = "${username}" +postgres_password = "${password:32}" +postgres_db = "timescaledb" +timescaledb_telemetry = "off" + +[config] +mounts = [] + +[config.env] +POSTGRES_USER = "${postgres_user}" +POSTGRES_PASSWORD = "${postgres_password}" +POSTGRES_DB = "${postgres_db}" +TIMESCALEDB_TELEMETRY = "${timescaledb_telemetry}" + +[[config.mounts]] +filePath = "001_create_timescaledb.sql" +content = """ +CREATE EXTENSION IF NOT EXISTS timescaledb; +""" + +[[config.mounts]] +filePath = "README.md" +content = """ +# TimescaleDB + +TimescaleDB is deployed as a PostgreSQL-compatible database on the internal `timescaledb:5432` service. + +Connection values: + +- Host: `timescaledb` +- Port: `5432` +- Database: `${postgres_db}` +- Username: `${postgres_user}` +- Password: generated by Dokploy + +The template creates the TimescaleDB extension during first initialization. PostgreSQL initialization variables only apply when the data volume is empty. + +Telemetry is disabled by default with `TIMESCALEDB_TELEMETRY=off`. The image runs `timescaledb-tune` automatically on first initialization and reads Docker cgroup limits when available. +""" diff --git a/blueprints/timescaledb/timescaledb.svg b/blueprints/timescaledb/timescaledb.svg new file mode 100644 index 00000000..d7bb10c4 --- /dev/null +++ b/blueprints/timescaledb/timescaledb.svg @@ -0,0 +1,10 @@ + + TimescaleDB + A layered database mark with a time-series pulse. + + + + + + + diff --git a/meta.json b/meta.json index 431878e4..19cc4f45 100644 --- a/meta.json +++ b/meta.json @@ -6634,6 +6634,25 @@ "uptime" ] }, + { + "id": "timescaledb", + "name": "TimescaleDB", + "version": "2.26.4-pg17", + "description": "TimescaleDB is an open-source time-series database packaged as a PostgreSQL extension, optimized for fast ingest and real-time analytics with standard SQL.", + "logo": "timescaledb.svg", + "links": { + "github": "https://github.com/timescale/timescaledb", + "website": "https://www.tigerdata.com/", + "docs": "https://docs.timescale.com/" + }, + "tags": [ + "database", + "postgresql", + "time-series", + "analytics", + "sql" + ] + }, { "id": "tolgee", "name": "Tolgee", From 5523024b9529e35bda51bb12d434fb559ae33939 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 8 Jul 2026 03:13:53 +0530 Subject: [PATCH 25/55] feat(appwrite): update template to 1.9.5 (#970) * feat(appwrite): update template to 1.9.5 Move default datastore to MongoDB (DocumentsDB), add worker-screenshots, worker-executions and task-interval, bump all images to 1.9.5, expand v5 function runtimes, and provide mongo bootstrap files via config.mounts. MariaDB kept as a supported alternative. PostgreSQL/embedding (VectorsDB) and bundled Traefik intentionally excluded. * fix(appwrite): re-add compose version header and add mongodb tag Address review: restore "version: 3.8" header (repo convention across blueprints) and add the mongodb tag to meta.json to reflect the new default datastore. * (fix): add container_name to appwrite executor so it can find itself The openruntimes executor v0.25 discovers its own container by listing containers whose name matches its hostname (exc1). Without container_name, compose names the container -openruntimes-executor-1, the lookup fails with 'Own container not found' and the executor crash-loops, breaking all function/site builds and executions with 'Could not resolve host: exc1'. Matches upstream appwrite 1.9.5 compose. Verified end-to-end on a fresh Dokploy v0.29.8 server: executor healthy, function build + execution succeed. * (fix): route wildcard sites/functions subdomains to appwrite Dokploy domains only support exact hosts, so Appwrite-generated preview and execution URLs (.sites., .functions.) hit Dokploy traefik with no matching router: 404 over HTTP and the traefik default self-signed certificate over HTTPS ('site is insecure'). Add HostRegexp routers on the appwrite service for *.sites and *.functions domains on both web and websecure entrypoints. HTTPS for these subdomains still requires the user to provide a wildcard certificate (DNS-01 resolver or uploaded wildcard/Origin CA cert); with tls=true routers in place traefik picks it up via SNI once added. Verified on a live Dokploy v0.29.8 deployment: site subdomains route correctly over HTTP and HTTPS after adding the labels. * (chore): remove wildcard routing comment * (fix): drop container_name, use service-name hostname for executor self-discovery container_name is disallowed by repo conventions and fails CI validation. The executor discovers its own container by matching gethostname() against Docker container names (substring filter), so hostname openruntimes-executor matches -openruntimes-executor-1 and _APP_EXECUTOR_HOST can use the DNS-resolvable service name. Also read the MongoDB healthcheck password from the container env (MONGO_INITDB_ROOT_PASSWORD) instead of interpolating _APP_DB_ROOT_PASS at render time, so shell-special characters in generated passwords cannot break authentication. Both verified on a live Dokploy v0.29.8 deployment: executor healthy, function execution succeeds, mongodb healthcheck passes. --------- Co-authored-by: ChiragAgg5k --- blueprints/appwrite/docker-compose.yml | 493 +++++++++++++++++++++---- blueprints/appwrite/template.toml | 145 +++++--- meta.json | 3 +- 3 files changed, 511 insertions(+), 130 deletions(-) diff --git a/blueprints/appwrite/docker-compose.yml b/blueprints/appwrite/docker-compose.yml index cb622284..2da23f52 100644 --- a/blueprints/appwrite/docker-compose.yml +++ b/blueprints/appwrite/docker-compose.yml @@ -8,12 +8,34 @@ x-logging: &x-logging max-size: "10m" services: appwrite: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 <<: *x-logging restart: unless-stopped labels: - traefik.enable=true - traefik.constraint-label-stack=appwrite + - 'traefik.http.routers.appwrite-sites-wildcard.rule=HostRegexp(`^.+\.${_APP_DOMAIN_SITES}$$`)' + - traefik.http.routers.appwrite-sites-wildcard.entrypoints=web + - traefik.http.routers.appwrite-sites-wildcard.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-sites-wildcard-secure.rule=HostRegexp(`^.+\.${_APP_DOMAIN_SITES}$$`)' + - traefik.http.routers.appwrite-sites-wildcard-secure.entrypoints=websecure + - traefik.http.routers.appwrite-sites-wildcard-secure.tls=true + - traefik.http.routers.appwrite-sites-wildcard-secure.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-functions-wildcard.rule=HostRegexp(`^.+\.${_APP_DOMAIN_FUNCTIONS}$$`)' + - traefik.http.routers.appwrite-functions-wildcard.entrypoints=web + - traefik.http.routers.appwrite-functions-wildcard.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-functions-wildcard-secure.rule=HostRegexp(`^.+\.${_APP_DOMAIN_FUNCTIONS}$$`)' + - traefik.http.routers.appwrite-functions-wildcard-secure.entrypoints=websecure + - traefik.http.routers.appwrite-functions-wildcard-secure.tls=true + - traefik.http.routers.appwrite-functions-wildcard-secure.service=appwrite-wildcard + - traefik.http.services.appwrite-wildcard.loadbalancer.server.port=80 + healthcheck: + test: + - CMD + - doctor + interval: 5s + timeout: 5s + retries: 12 volumes: - appwrite-uploads:/storage/uploads:rw - appwrite-imports:/storage/imports:rw @@ -24,21 +46,25 @@ services: - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw depends_on: - - mariadb - redis - # - clamav + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_EDITION - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_LOCALE + - _APP_COMPRESSION_ENABLED - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_CONSOLE_WHITELIST_ROOT - _APP_CONSOLE_WHITELIST_EMAILS - _APP_CONSOLE_SESSION_ALERTS - _APP_CONSOLE_WHITELIST_IPS - _APP_CONSOLE_HOSTNAMES + - _APP_CONSOLE_SCHEMA - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_SYSTEM_TEAM_EMAIL - _APP_EMAIL_SECURITY - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE @@ -47,6 +73,8 @@ services: - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_CONSOLE_TRUSTED_PROJECTS - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A @@ -57,11 +85,18 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -108,8 +143,6 @@ services: - _APP_EXECUTOR_HOST - _APP_LOGGING_CONFIG - _APP_MAINTENANCE_INTERVAL - - _APP_MAINTENANCE_DELAY - - _APP_MAINTENANCE_START_TIME - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_ABUSE @@ -119,6 +152,7 @@ services: - _APP_MAINTENANCE_RETENTION_SCHEDULES - _APP_SMS_PROVIDER - _APP_SMS_FROM + - _APP_GRAPHQL_INTROSPECTION - _APP_GRAPHQL_MAX_BATCH_SIZE - _APP_GRAPHQL_MAX_COMPLEXITY - _APP_GRAPHQL_MAX_DEPTH @@ -131,16 +165,26 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_ASSISTANT_OPENAI_API_KEY + - _APP_CONSOLE_COUNTRIES_DENYLIST + - _APP_EXPERIMENT_LOGGING_PROVIDER + - _APP_EXPERIMENT_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + - _APP_DATABASE_SHARED_NAMESPACE + - _APP_FUNCTIONS_CREATION_ABUSE_LIMIT + - _APP_CUSTOM_DOMAIN_DENY_LIST + - _APP_TRUSTED_HEADERS + - _APP_MIGRATION_HOST + appwrite-console: <<: *x-logging - image: appwrite/console:7.4.7 + image: appwrite/console:8.7.5 restart: unless-stopped labels: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" appwrite-realtime: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: realtime <<: *x-logging restart: unless-stopped @@ -148,7 +192,7 @@ services: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV @@ -160,51 +204,69 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_USAGE_STATS - _APP_LOGGING_CONFIG + - _APP_LOGGING_CONFIG_REALTIME + - _APP_DATABASE_SHARED_TABLES + - _APP_POOL_ADAPTER=swoole appwrite-worker-audits: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-audits <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES appwrite-worker-webhooks: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-webhooks <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_EMAIL_SECURITY - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -215,15 +277,17 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_LOGGING_CONFIG + - _APP_WEBHOOK_MAX_FAILED_ATTEMPTS + - _APP_DATABASE_SHARED_TABLES appwrite-worker-deletes: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-deletes <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-uploads:/storage/uploads:rw - appwrite-cache:/storage/cache:rw @@ -233,12 +297,16 @@ services: - appwrite-certificates:/storage/certificates:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -269,52 +337,64 @@ services: - _APP_LOGGING_CONFIG - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_DATABASE_SHARED_TABLES + - _APP_EMAIL_CERTIFICATES - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - - _APP_MAINTENANCE_RETENTION_EXECUTION - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - - _APP_EMAIL_CERTIFICATES appwrite-worker-databases: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-databases <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_LOGGING_CONFIG + - _APP_QUEUE_NAME + - _APP_DATABASE_SHARED_TABLES appwrite-worker-builds: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-builds <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-functions:/storage/functions:rw - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw - - appwrite-uploads:/storage/uploads:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST @@ -322,6 +402,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -336,10 +417,13 @@ services: - _APP_COMPUTE_BUILD_TIMEOUT - _APP_COMPUTE_CPUS - _APP_COMPUTE_MEMORY + - _APP_OPEN_RUNTIMES_NFT - _APP_COMPUTE_SIZE_LIMIT - _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_CONSOLE_TRUSTED_PROJECTS - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -362,22 +446,85 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DATABASE_SHARED_TABLES - _APP_DOMAIN_SITES + extra_hosts: + - host.docker.internal:host-gateway + + appwrite-worker-screenshots: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-screenshots + <<: *x-logging + restart: unless-stopped + depends_on: + - redis + - ${_APP_DB_HOST:-mongodb} + volumes: + - appwrite-uploads:/storage/uploads:rw + environment: + - _APP_BROWSER_HOST + - _APP_WORKER_SCREENSHOTS_ROUTER + - _APP_OPTIONS_FORCE_HTTPS + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_LOGGING_CONFIG + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + extra_hosts: + - host.docker.internal:host-gateway appwrite-worker-certificates: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-certificates <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME @@ -386,30 +533,64 @@ services: - _APP_DOMAIN_TARGET_CAA - _APP_DNS - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES - appwrite-worker-functions: - image: appwrite/appwrite:1.8.0 - entrypoint: worker-functions + appwrite-worker-executions: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-executions <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} + environment: + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-functions: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-functions + <<: *x-logging + restart: unless-stopped + depends_on: + - ${_APP_DB_HOST:-mongodb} + - redis - openruntimes-executor environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_OPTIONS_FORCE_HTTPS @@ -417,6 +598,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -433,9 +615,11 @@ services: - _APP_DOCKER_HUB_USERNAME - _APP_DOCKER_HUB_PASSWORD - _APP_LOGGING_CONFIG + - _APP_LOGGING_PROVIDER + - _APP_DATABASE_SHARED_TABLES appwrite-worker-mails: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-mails <<: *x-logging restart: unless-stopped @@ -443,19 +627,23 @@ services: - redis environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -464,9 +652,10 @@ services: - _APP_LOGGING_CONFIG - _APP_DOMAIN - _APP_OPTIONS_FORCE_HTTPS + - _APP_DATABASE_SHARED_TABLES appwrite-worker-messaging: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-messaging <<: *x-logging restart: unless-stopped @@ -476,12 +665,16 @@ services: - redis environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -490,6 +683,7 @@ services: - _APP_LOGGING_CONFIG - _APP_SMS_FROM - _APP_SMS_PROVIDER + - _APP_SMS_PROJECTS_DENY_LIST - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -512,19 +706,24 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DATABASE_SHARED_TABLES appwrite-worker-migrations: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-migrations <<: *x-logging restart: unless-stopped volumes: - appwrite-imports:/storage/imports:rw + - appwrite-uploads:/storage/uploads:rw depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME @@ -537,6 +736,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -545,17 +745,22 @@ services: - _APP_LOGGING_CONFIG - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + - _APP_DATABASE_SHARED_TABLES + - _APP_OPTIONS_FORCE_HTTPS + - _APP_MIGRATION_HOST appwrite-task-maintenance: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: maintenance <<: *x-logging restart: unless-stopped depends_on: + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA @@ -568,6 +773,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -581,19 +787,58 @@ services: - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_MAINTENANCE_START_TIME + - _APP_DATABASE_SHARED_TABLES + + appwrite-task-interval: + image: appwrite/appwrite:1.9.5 + entrypoint: interval + <<: *x-logging + restart: unless-stopped + depends_on: + - ${_APP_DB_HOST:-mongodb} + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES + - _APP_INTERVAL_DOMAIN_VERIFICATION + - _APP_INTERVAL_CLEANUP_STALE_EXECUTIONS appwrite-task-stats-resources: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: stats-resources <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -609,42 +854,21 @@ services: - _APP_STATS_RESOURCES_INTERVAL appwrite-worker-stats-resources: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-stats-resources <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_CONFIG - - _APP_STATS_RESOURCES_INTERVAL - - appwrite-worker-stats-usage: - image: appwrite/appwrite:1.8.0 - entrypoint: worker-stats-usage - <<: *x-logging - restart: unless-stopped - depends_on: - - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -657,91 +881,145 @@ services: - _APP_USAGE_STATS - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-stats-usage: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-stats-usage + <<: *x-logging + restart: unless-stopped + depends_on: + - redis + - ${_APP_DB_HOST:-mongodb} + environment: + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-functions: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-functions <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_OPTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_MIGRATION_HOST + - _APP_CONSOLE_SCHEMA - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-executions: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-executions <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_OPTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_MIGRATION_HOST + - _APP_CONSOLE_SCHEMA - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-messages: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-messages <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-assistant: - image: appwrite/assistant:0.8.3 + image: appwrite/assistant:0.8.4 <<: *x-logging restart: unless-stopped environment: - _APP_ASSISTANT_OPENAI_API_KEY appwrite-browser: - image: appwrite/browser:0.2.4 + image: appwrite/browser:0.3.2 <<: *x-logging restart: unless-stopped openruntimes-executor: - hostname: exc1 + hostname: openruntimes-executor <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.7.22 + image: openruntimes/executor:0.25.1 volumes: - /var/run/docker.sock:/var/run/docker.sock - appwrite-builds:/storage/builds:rw @@ -751,7 +1029,10 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: - - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD + - OPR_EXECUTOR_CONNECTION_STORAGE=local://localhost + - OPR_EXECUTOR_CONNECTION_BUILD_CACHE_STORAGE= + - OPR_EXECUTOR_IMAGES=$_APP_EXECUTOR_IMAGES + - OPR_EXECUTOR_INACTIVE_THRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME @@ -760,6 +1041,7 @@ services: - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET - OPR_EXECUTOR_RUNTIME_VERSIONS=v5 + - OPEN_RUNTIMES_NFT=$_APP_OPEN_RUNTIMES_NFT - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY @@ -783,6 +1065,55 @@ services: - OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET - OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET + healthcheck: + test: + - CMD-SHELL + - 'curl -fsS -H "Authorization: Bearer $$OPR_EXECUTOR_SECRET" http://localhost/v1/health >/dev/null' + interval: 5s + timeout: 3s + retries: 20 + start_period: 5s + + mongodb: + image: mongo:8.2.5 + <<: *x-logging + restart: unless-stopped + volumes: + - appwrite-mongodb:/data/db + - appwrite-mongodb-keyfile:/data/keyfile + - ../files/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro + - ../files/mongo-entrypoint.sh:/mongo-entrypoint.sh:ro + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} + - MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA} + - MONGO_INITDB_USERNAME=${_APP_DB_USER} + - MONGO_INITDB_PASSWORD=${_APP_DB_PASS} + entrypoint: + - /bin/bash + - /mongo-entrypoint.sh + healthcheck: + test: | + bash -c " + mongosh -u root -p \"$$MONGO_INITDB_ROOT_PASSWORD\" --authenticationDatabase admin --quiet --eval \" + try { + const hello = db.adminCommand({hello: 1}); + if (hello.isWritablePrimary) { + quit(0); + } + } catch (e) {} + + try { + rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongodb:27017'}]}); + } catch (e) {} + + quit(1); + \" 2>/dev/null + " + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s mariadb: image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p @@ -799,7 +1130,7 @@ services: command: "mysqld --innodb-flush-method=fsync" redis: - image: redis:7.2.4-alpine + image: redis:7.4.7-alpine <<: *x-logging restart: unless-stopped command: > @@ -818,6 +1149,8 @@ services: volumes: appwrite-mariadb: + appwrite-mongodb: + appwrite-mongodb-keyfile: appwrite-redis: appwrite-cache: appwrite-uploads: diff --git a/blueprints/appwrite/template.toml b/blueprints/appwrite/template.toml index cca2d43d..54d3a8bc 100644 --- a/blueprints/appwrite/template.toml +++ b/blueprints/appwrite/template.toml @@ -10,58 +10,69 @@ executor_secret = "${password:32}" [config] env = [ "_APP_ENV=production", + "_APP_EDITION=self-hosted", "_APP_LOCALE=en", + "_APP_POOL_ADAPTER=stack", + "_APP_WORKER_PER_CORE=6", "_APP_OPTIONS_ABUSE=enabled", "_APP_OPTIONS_FORCE_HTTPS=disabled", - "_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_PROTECTION=disabled", "_APP_OPENSSL_KEY_V1=${openssl_key}", "_APP_DOMAIN=${main_domain}", + "_APP_CONSOLE_DOMAIN=${main_domain}", + "_APP_CONSOLE_TRUSTED_PROJECTS=", "_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com", "_APP_DOMAIN_FUNCTIONS=${functions_domain}", "_APP_DOMAIN_SITES=${sites_domain}", - "_APP_DOMAIN_TARGET=localhost", "_APP_DOMAIN_TARGET_CNAME=localhost", "_APP_DOMAIN_TARGET_AAAA=::1", "_APP_DOMAIN_TARGET_A=127.0.0.1", "_APP_DOMAIN_TARGET_CAA=", "_APP_DNS=8.8.8.8", + "_APP_TRUSTED_HEADERS=x-forwarded-for", "_APP_CONSOLE_WHITELIST_ROOT=enabled", "_APP_CONSOLE_WHITELIST_EMAILS=", "_APP_CONSOLE_WHITELIST_IPS=", "_APP_CONSOLE_HOSTNAMES=", + "_APP_CONSOLE_SCHEMA=appwriteio", + "_APP_CONSOLE_SESSION_ALERTS=disabled", + "_APP_CONSOLE_COUNTRIES_DENYLIST=", "_APP_SYSTEM_EMAIL_NAME=Appwrite", "_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io", "_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io", "_APP_SYSTEM_RESPONSE_FORMAT=", - "_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io", "_APP_EMAIL_SECURITY=", "_APP_EMAIL_CERTIFICATES=", - "_APP_USAGE_STATS=enabled", - "_APP_LOGGING_PROVIDER=", - "_APP_LOGGING_CONFIG=", - "_APP_USAGE_AGGREGATION_INTERVAL=30", - "_APP_USAGE_TIMESERIES_INTERVAL=30", - "_APP_USAGE_DATABASE_INTERVAL=900", - "_APP_WORKER_PER_CORE=6", - "_APP_CONSOLE_SESSION_ALERTS=disabled", "_APP_COMPRESSION_ENABLED=enabled", "_APP_COMPRESSION_MIN_SIZE_BYTES=1024", + "_APP_USAGE_STATS=enabled", + "_APP_USAGE_AGGREGATION_INTERVAL=30", + "_APP_STATS_RESOURCES_INTERVAL=30", + "_APP_LOGGING_PROVIDER=", + "_APP_LOGGING_CONFIG=", + "_APP_LOGGING_CONFIG_REALTIME=", + "_APP_EXPERIMENT_LOGGING_PROVIDER=", + "_APP_EXPERIMENT_LOGGING_CONFIG=", "_APP_REDIS_HOST=redis", "_APP_REDIS_PORT=6379", "_APP_REDIS_USER=", "_APP_REDIS_PASS=", - "_APP_DB_HOST=mariadb", - "_APP_DB_PORT=3306", + "_APP_DB_ADAPTER=mongodb", + "_APP_DB_HOST=mongodb", + "_APP_DB_PORT=27017", "_APP_DB_SCHEMA=appwrite", "_APP_DB_USER=user", "_APP_DB_PASS=${db_user_pw}", "_APP_DB_ROOT_PASS=${db_root_pw}", - "_APP_INFLUXDB_HOST=influxdb", - "_APP_INFLUXDB_PORT=8086", - "_APP_STATSD_HOST=telegraf", - "_APP_STATSD_PORT=8125", + "_APP_DATABASE_SHARED_TABLES=", + "_APP_DATABASE_SHARED_NAMESPACE=", + "_APP_DB_ADAPTER_VECTORSDB=", + "_APP_DB_HOST_VECTORSDB=", + "_APP_DB_PORT_VECTORSDB=", + "_APP_DB_SCHEMA_VECTORSDB=", + "_APP_DB_USER_VECTORSDB=", + "_APP_DB_PASS_VECTORSDB=", "_APP_SMTP_HOST=", "_APP_SMTP_PORT=", "_APP_SMTP_SECURE=", @@ -69,6 +80,7 @@ env = [ "_APP_SMTP_PASSWORD=", "_APP_SMS_PROVIDER=", "_APP_SMS_FROM=", + "_APP_SMS_PROJECTS_DENY_LIST=", "_APP_STORAGE_LIMIT=30000000", "_APP_STORAGE_PREVIEW_LIMIT=20000000", "_APP_STORAGE_ANTIVIRUS=disabled", @@ -96,45 +108,27 @@ env = [ "_APP_STORAGE_WASABI_SECRET=", "_APP_STORAGE_WASABI_REGION=eu-central-1", "_APP_STORAGE_WASABI_BUCKET=", - "_APP_FUNCTIONS_SIZE_LIMIT=30000000", "_APP_COMPUTE_SIZE_LIMIT=30000000", - "_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000", - "_APP_FUNCTIONS_TIMEOUT=900", - "_APP_FUNCTIONS_BUILD_TIMEOUT=900", "_APP_COMPUTE_BUILD_TIMEOUT=900", - "_APP_FUNCTIONS_CONTAINERS=10", - "_APP_FUNCTIONS_CPUS=0", "_APP_COMPUTE_CPUS=0", - "_APP_FUNCTIONS_MEMORY=0", "_APP_COMPUTE_MEMORY=0", - "_APP_FUNCTIONS_MEMORY_SWAP=0", - "_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0", - "_APP_EXECUTOR_SECRET=${executor_secret}", - "_APP_EXECUTOR_HOST=http://exc1/v1", - "_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0", - "_APP_FUNCTIONS_INACTIVE_THRESHOLD=60", "_APP_COMPUTE_INACTIVE_THRESHOLD=60", - "DOCKERHUB_PULL_USERNAME=", - "DOCKERHUB_PULL_PASSWORD=", - "DOCKERHUB_PULL_EMAIL=", - "OPEN_RUNTIMES_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_RUNTIMES_NETWORK=dokploy-network", + "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", "_APP_COMPUTE_RUNTIMES_NETWORK=dokploy-network", + "_APP_FUNCTIONS_TIMEOUT=900", + "_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000", + "_APP_FUNCTIONS_RUNTIMES=node-22,python-3.12,php-8.3,ruby-3.3,dart-3.5,go-1.23,rust-1.83,bun-1.1,deno-1.46,java-21.0,dotnet-8.0", + "_APP_SITES_TIMEOUT=900", + "_APP_SITES_RUNTIMES=static-1,node-22", + "_APP_EXECUTOR_SECRET=${executor_secret}", + "_APP_EXECUTOR_HOST=http://openruntimes-executor/v1", + "_APP_EXECUTOR_IMAGES=openruntimes/node:v5-22,openruntimes/python:v5-3.12,openruntimes/php:v5-8.3,openruntimes/static:v5-1", + "_APP_OPEN_RUNTIMES_NFT=enabled", + "_APP_BROWSER_HOST=http://appwrite-browser:3000/v1", + "_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite", "_APP_DOCKER_HUB_USERNAME=", "_APP_DOCKER_HUB_PASSWORD=", - "_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600", - "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", - "_APP_SITES_TIMEOUT=900", - "_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29", - "_APP_VCS_GITHUB_APP_NAME=", - "_APP_VCS_GITHUB_PRIVATE_KEY=", - "_APP_VCS_GITHUB_APP_ID=", - "_APP_VCS_GITHUB_CLIENT_ID=", - "_APP_VCS_GITHUB_CLIENT_SECRET=", - "_APP_VCS_GITHUB_WEBHOOK_SECRET=", "_APP_MAINTENANCE_INTERVAL=86400", - "_APP_MAINTENANCE_DELAY=0", "_APP_MAINTENANCE_START_TIME=00:00", "_APP_MAINTENANCE_RETENTION_CACHE=2592000", "_APP_MAINTENANCE_RETENTION_EXECUTION=1209600", @@ -143,14 +137,67 @@ env = [ "_APP_MAINTENANCE_RETENTION_ABUSE=86400", "_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000", "_APP_MAINTENANCE_RETENTION_SCHEDULES=86400", + "_APP_INTERVAL_DOMAIN_VERIFICATION=120", + "_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300", + "_APP_GRAPHQL_INTROSPECTION=enabled", "_APP_GRAPHQL_MAX_BATCH_SIZE=10", "_APP_GRAPHQL_MAX_COMPLEXITY=250", - "_APP_GRAPHQL_MAX_DEPTH=3", + "_APP_GRAPHQL_MAX_DEPTH=4", + "_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10", + "_APP_VCS_GITHUB_APP_NAME=", + "_APP_VCS_GITHUB_PRIVATE_KEY=", + "_APP_VCS_GITHUB_APP_ID=", + "_APP_VCS_GITHUB_CLIENT_ID=", + "_APP_VCS_GITHUB_CLIENT_SECRET=", + "_APP_VCS_GITHUB_WEBHOOK_SECRET=", + "_APP_MIGRATION_HOST=appwrite", "_APP_MIGRATIONS_FIREBASE_CLIENT_ID=", "_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=", "_APP_ASSISTANT_OPENAI_API_KEY=", ] -mounts = [] + +[[config.mounts]] +filePath = "mongo-init.js" +content = """// mongo-init.js + +// Switch to the admin database +const adminDb = db.getSiblingDB('admin'); + +// Get username and password from environment variables +const username = process.env.MONGO_INITDB_USERNAME; +const password = process.env.MONGO_INITDB_PASSWORD; +const database = process.env.MONGO_INITDB_DATABASE; + +// Create the user +adminDb.createUser({ + user: username, + pwd: password, + roles: [ + { role: 'readWrite', db: database } + ] +}); +""" + +[[config.mounts]] +filePath = "mongo-entrypoint.sh" +content = """#!/bin/bash +set -e + +# Fix keyfile permissions if mounted from volume +KEYFILE_PATH="/data/keyfile/mongo-keyfile" + +if [ ! -f "$KEYFILE_PATH" ]; then + echo "Generating random MongoDB keyfile..." + mkdir -p /data/keyfile + openssl rand -base64 756 > "$KEYFILE_PATH" +fi + +chmod 400 "$KEYFILE_PATH" +chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH" + +# Use MongoDB's standard entrypoint with our command +exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH" +""" [[config.domains]] serviceName = "appwrite" diff --git a/meta.json b/meta.json index 19cc4f45..452ceff6 100644 --- a/meta.json +++ b/meta.json @@ -399,7 +399,7 @@ { "id": "appwrite", "name": "Appwrite", - "version": "1.8.0", + "version": "1.9.5", "description": "Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", "links": { "github": "https://github.com/appwrite/appwrite", @@ -411,6 +411,7 @@ "database", "firebase", "mariadb", + "mongodb", "hosting", "self-hosted" ] From a33fa76399456f6ab82bb97b12dab8745aab3cfd Mon Sep 17 00:00:00 2001 From: Cagdas Salur Date: Wed, 8 Jul 2026 00:43:56 +0300 Subject: [PATCH 26/55] =?UTF-8?q?feat(template):=20add=20tela=20=E2=80=94?= =?UTF-8?q?=20markdown-native=20team=20wiki=20with=20MCP=20(#967)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blueprints/tela/docker-compose.yml | 65 +++++++++++++++++++++++++++++ blueprints/tela/logo.png | Bin 0 -> 11508 bytes blueprints/tela/template.toml | 18 ++++++++ meta.json | 20 +++++++++ 4 files changed, 103 insertions(+) create mode 100644 blueprints/tela/docker-compose.yml create mode 100644 blueprints/tela/logo.png create mode 100644 blueprints/tela/template.toml diff --git a/blueprints/tela/docker-compose.yml b/blueprints/tela/docker-compose.yml new file mode 100644 index 00000000..a5117484 --- /dev/null +++ b/blueprints/tela/docker-compose.yml @@ -0,0 +1,65 @@ +services: + proxy: + image: ghcr.io/zcag/tela-proxy:0.7.0 + restart: unless-stopped + environment: + - TELA_SITE_ADDRESS=:80 + - TELA_UPSTREAM_BACKEND=backend:8080 + - TELA_UPSTREAM_FRONTEND=frontend:80 + depends_on: + - backend + - frontend + volumes: + - caddy-data:/data + - caddy-config:/config + + backend: + image: ghcr.io/zcag/tela-backend:0.7.0 + restart: unless-stopped + environment: + - TELA_PUBLIC_BASE_URL=${TELA_PUBLIC_BASE_URL} + - TELA_DATABASE_URL=postgres://tela:${TELA_PG_PASSWORD}@postgres:5432/tela?sslmode=disable + - TELA_SHARE_SECRET=${TELA_SHARE_SECRET} + - TELA_API_KEY_SECRET=${TELA_API_KEY_SECRET} + - TELA_DECK_URL=http://deck:3344 + - TELA_GOTENBERG_URL=http://gotenberg:3000 + - TELA_PDF_RENDER_BASE_URL=http://proxy + depends_on: + postgres: + condition: service_healthy + + frontend: + image: ghcr.io/zcag/tela-frontend:0.7.0 + restart: unless-stopped + + postgres: + image: pgvector/pgvector:pg17 + restart: unless-stopped + environment: + - POSTGRES_USER=tela + - POSTGRES_PASSWORD=${TELA_PG_PASSWORD} + - POSTGRES_DB=tela + healthcheck: + test: ["CMD-SHELL", "pg_isready -U tela -d tela"] + interval: 5s + timeout: 5s + retries: 20 + start_period: 10s + volumes: + - pgdata:/var/lib/postgresql/data + + deck: + image: ghcr.io/zcag/tela-deck:0.7.0 + restart: unless-stopped + volumes: + - deckcache:/data + + gotenberg: + image: gotenberg/gotenberg:8 + restart: unless-stopped + +volumes: + pgdata: + deckcache: + caddy-data: + caddy-config: diff --git a/blueprints/tela/logo.png b/blueprints/tela/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8c90d578b38099e70398e98ce0465f61c0ed25cd GIT binary patch literal 11508 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
bM3cY4zB<6GWlR&rQW0+OrqiB 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 PWuW*#?S7H0*^B=MRX Date: Wed, 8 Jul 2026 01:43:59 +0400 Subject: [PATCH 27/55] feat: add Speedtest Tracker template (#952) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/speedtest-tracker/docker-compose.yml | 16 ++++++++++++++++ .../speedtest-tracker/speedtest-tracker.svg | 5 +++++ blueprints/speedtest-tracker/template.toml | 16 ++++++++++++++++ meta.json | 17 +++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 blueprints/speedtest-tracker/docker-compose.yml create mode 100644 blueprints/speedtest-tracker/speedtest-tracker.svg create mode 100644 blueprints/speedtest-tracker/template.toml diff --git a/blueprints/speedtest-tracker/docker-compose.yml b/blueprints/speedtest-tracker/docker-compose.yml new file mode 100644 index 00000000..234eddb4 --- /dev/null +++ b/blueprints/speedtest-tracker/docker-compose.yml @@ -0,0 +1,16 @@ +services: + speedtest-tracker: + image: lscr.io/linuxserver/speedtest-tracker:latest + restart: unless-stopped + environment: + PUID: ${PUID} + PGID: ${PGID} + APP_KEY: ${APP_KEY} + DB_CONNECTION: sqlite + volumes: + - speedtest_config:/config + ports: + - "80" + +volumes: + speedtest_config: diff --git a/blueprints/speedtest-tracker/speedtest-tracker.svg b/blueprints/speedtest-tracker/speedtest-tracker.svg new file mode 100644 index 00000000..1928bbb7 --- /dev/null +++ b/blueprints/speedtest-tracker/speedtest-tracker.svg @@ -0,0 +1,5 @@ + + + + S + diff --git a/blueprints/speedtest-tracker/template.toml b/blueprints/speedtest-tracker/template.toml new file mode 100644 index 00000000..2ae03d8c --- /dev/null +++ b/blueprints/speedtest-tracker/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "speedtest-tracker" +port = 80 +host = "${main_domain}" + +[config.env] +APP_KEY = "base64:${app_key}" +PUID = "1000" +PGID = "1000" diff --git a/meta.json b/meta.json index d0feeda5..03578429 100644 --- a/meta.json +++ b/meta.json @@ -6370,6 +6370,23 @@ "storage" ] }, + { + "id": "speedtest-tracker", + "name": "Speedtest Tracker", + "version": "latest", + "description": "Speedtest Tracker runs scheduled internet speed tests and stores the results in a self-hosted dashboard.", + "logo": "speedtest-tracker.svg", + "links": { + "github": "https://github.com/alexjustesen/speedtest-tracker", + "website": "https://docs.speedtest-tracker.dev/", + "docs": "https://docs.speedtest-tracker.dev/" + }, + "tags": [ + "monitoring", + "network", + "speedtest" + ] + }, { "id": "spliit", "name": "Spliit", From cdc72377c7262a76c72e0f833f338b3fdb661e25 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 01:44:04 +0400 Subject: [PATCH 28/55] fix: respect stopped Immich deployments (#938) Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> --- blueprints/immich/docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/blueprints/immich/docker-compose.yml b/blueprints/immich/docker-compose.yml index 8f12d6d1..f385b329 100644 --- a/blueprints/immich/docker-compose.yml +++ b/blueprints/immich/docker-compose.yml @@ -28,7 +28,7 @@ services: REDIS_DBINDEX: ${REDIS_DBINDEX} # Server Configuration TZ: ${TZ} - restart: always + restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:2283/server-info/ping"] interval: 30s @@ -44,7 +44,7 @@ services: REDIS_HOSTNAME: ${REDIS_HOSTNAME} REDIS_PORT: ${REDIS_PORT} REDIS_DBINDEX: ${REDIS_DBINDEX} - restart: always + restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3003/ping"] interval: 30s @@ -61,7 +61,7 @@ services: interval: 10s timeout: 5s retries: 5 - restart: always + restart: unless-stopped immich-database: image: tensorchord/pgvecto-rs:pg14-v0.3.0 @@ -94,10 +94,10 @@ services: '-c', 'wal_compression=on', ] - restart: always + restart: unless-stopped volumes: immich-model-cache: immich-postgres: immich-library: - immich-redis-data: + immich-redis-data: From 1c0380d77b4e2ba2316384964308e84a2aeb0b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20G=C3=BCndo=C4=9Fdu?= Date: Wed, 8 Jul 2026 00:44:07 +0300 Subject: [PATCH 29/55] feat(blueprint): add LibreDB Studio (#931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add libredb-studio compose Adds LibreDB Studio — an MIT-licensed, AI-powered open-source SQL IDE (PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB, Redis). This commit adds the docker-compose.yml. The template.toml and meta.json entry follow in the same branch. Image: ghcr.io/libredb/libredb-studio Repo: https://github.com/libredb/libredb-studio * feat: add libredb-studio template.toml Adds the Dokploy template.toml for LibreDB Studio. Defines auto-generated secrets (admin password, user password, JWT secret), exposes port 3000 on the primary domain, and persists data via a mount at /app/data. * feat: add libredb-studio to meta.json Registers LibreDB Studio in the template catalog index (meta.json), alphabetically-independent top entry. Completes the LibreDB Studio template (compose + template.toml + meta entry). * fix: sort libredb-studio entry alphabetically in meta.json Moves the libredb-studio entry to its correct alphabetical position between librechat and libredesk so meta.json remains sorted by id. Resolves the failing "Validate and Process Meta.json" check (duplicates/sort-order validation). * fix: expand libredb-studio tags to match meta.json formatting Formats the libredb-studio "tags" array as a multi-line block to match the output of dedupe-and-sort-meta.js, making the entry byte-identical to the canonical processed file. Resolves the failing "Validate and Process Meta.json" check. * fix: remove invalid mounts block from libredb-studio template.toml Removes the [[config.mounts]] block, which failed validation because Dokploy requires a 'content' field for file mounts. Data persistence is already handled by the named volume (libredb-data:/app/data) in docker-compose.yml, so no file mount is needed. Resolves the failing "Validate Docker Compose Files" check. * fix: correct indentation of libredb-studio tags in meta.json Adjusts the indentation of the "tags" key to 4 spaces so the libredb-studio entry is byte-identical to the output of dedupe-and-sort-meta.js. Resolves the remaining "Validate and Process Meta.json" check failure. * feat: add libredb-studio logo Adds the libredb-studio.svg logo to blueprints/libredb-studio/ so it matches the "logo" field in meta.json. Resolves the "Validate Blueprints Structure and Meta" check, which verifies that each meta.json logo has a corresponding file in its blueprint folder. * fix: bump libredb-studio image to v0.9.27 Updates the image tag from v0.9.13 to v0.9.27 (current release) so reviewers can test against the latest version. * fix: update LibreDB Studio version to v0.9.27 Update version field from v0.9.13 to v0.9.27 in meta.json * fix(blueprint): remove v prefix from image tag GHCR tags do not use v prefix (0.9.27 not v0.9.27) * fix(meta): remove v prefix from version field GHCR tags do not use v prefix (0.9.27 not v0.9.27) --- blueprints/libredb-studio/docker-compose.yml | 17 +++++++++++ blueprints/libredb-studio/libredb-studio.svg | 32 ++++++++++++++++++++ blueprints/libredb-studio/template.toml | 18 +++++++++++ meta.json | 16 ++++++++++ 4 files changed, 83 insertions(+) create mode 100644 blueprints/libredb-studio/docker-compose.yml create mode 100644 blueprints/libredb-studio/libredb-studio.svg create mode 100644 blueprints/libredb-studio/template.toml diff --git a/blueprints/libredb-studio/docker-compose.yml b/blueprints/libredb-studio/docker-compose.yml new file mode 100644 index 00000000..00b88766 --- /dev/null +++ b/blueprints/libredb-studio/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" +services: + libredb-studio: + image: ghcr.io/libredb/libredb-studio:0.9.27 + restart: unless-stopped + environment: + - ADMIN_EMAIL=admin@libredb.org + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - USER_EMAIL=user@libredb.org + - USER_PASSWORD=${USER_PASSWORD} + - JWT_SECRET=${JWT_SECRET} + - STORAGE_PROVIDER=sqlite + - STORAGE_SQLITE_PATH=/app/data/libredb-storage.db + volumes: + - libredb-data:/app/data +volumes: + libredb-data: diff --git a/blueprints/libredb-studio/libredb-studio.svg b/blueprints/libredb-studio/libredb-studio.svg new file mode 100644 index 00000000..c36a98eb --- /dev/null +++ b/blueprints/libredb-studio/libredb-studio.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/libredb-studio/template.toml b/blueprints/libredb-studio/template.toml new file mode 100644 index 00000000..9d30084d --- /dev/null +++ b/blueprints/libredb-studio/template.toml @@ -0,0 +1,18 @@ +[variables] +admin_password = "${password:32}" +user_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +[[config.domains]] +serviceName = "libredb-studio" +port = 3000 + +[[config.env]] +ADMIN_PASSWORD = "${admin_password}" + +[[config.env]] +USER_PASSWORD = "${user_password}" + +[[config.env]] +JWT_SECRET = "${jwt_secret}" diff --git a/meta.json b/meta.json index 03578429..c8a04988 100644 --- a/meta.json +++ b/meta.json @@ -3884,6 +3884,22 @@ "generative-ai" ] }, + { + "id": "libredb-studio", + "name": "LibreDB Studio", + "version": "0.9.27", + "description": "The modern, AI-powered open-source SQL IDE. Query PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB and Redis from your browser.", + "logo": "libredb-studio.svg", + "links": { + "github": "https://github.com/libredb/libredb-studio", + "website": "https://libredb.org/", + "docs": "https://libredb.org/" + }, + "tags": [ + "database", + "sql" + ] + }, { "id": "libredesk", "name": "Libredesk", From f7960475521ec824dcf2b3c84334dbce0d2a4b58 Mon Sep 17 00:00:00 2001 From: Rohit Mulani Date: Wed, 8 Jul 2026 01:44:10 +0400 Subject: [PATCH 30/55] feat: add Prefect template (#930) * feat: add Prefect template * fix: tighten Prefect template routing --------- Co-authored-by: Rohit Mulani <289630555+rohitmulani63-ops@users.noreply.github.com> Co-authored-by: Mauricio Siu --- blueprints/prefect/docker-compose.yml | 90 +++++++++++++++++++++++++++ blueprints/prefect/prefect.svg | 7 +++ blueprints/prefect/template.toml | 15 +++++ meta.json | 17 +++++ 4 files changed, 129 insertions(+) create mode 100644 blueprints/prefect/docker-compose.yml create mode 100644 blueprints/prefect/prefect.svg create mode 100644 blueprints/prefect/template.toml diff --git a/blueprints/prefect/docker-compose.yml b/blueprints/prefect/docker-compose.yml new file mode 100644 index 00000000..242936e8 --- /dev/null +++ b/blueprints/prefect/docker-compose.yml @@ -0,0 +1,90 @@ +version: "3.8" + +services: + prefect: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect-postgres: + condition: service_healthy + prefect-redis: + condition: service_healthy + environment: + - PREFECT_API_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:${POSTGRES_PASSWORD}@prefect-postgres:5432/prefect + - PREFECT_SERVER_API_HOST=0.0.0.0 + - PREFECT_SERVER_UI_API_URL=https://${PREFECT_DOMAIN}/api + - PREFECT_MESSAGING_BROKER=prefect_redis.messaging + - PREFECT_MESSAGING_CACHE=prefect_redis.messaging + - PREFECT_REDIS_MESSAGING_HOST=prefect-redis + - PREFECT_REDIS_MESSAGING_PORT=6379 + - PREFECT_REDIS_MESSAGING_DB=0 + command: prefect server start --no-services + expose: + - 4200 + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request as u; u.urlopen('http://localhost:4200/api/health', timeout=1)", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + prefect-services: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect: + condition: service_healthy + environment: + - PREFECT_API_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:${POSTGRES_PASSWORD}@prefect-postgres:5432/prefect + - PREFECT_MESSAGING_BROKER=prefect_redis.messaging + - PREFECT_MESSAGING_CACHE=prefect_redis.messaging + - PREFECT_REDIS_MESSAGING_HOST=prefect-redis + - PREFECT_REDIS_MESSAGING_PORT=6379 + - PREFECT_REDIS_MESSAGING_DB=0 + command: prefect server services start + + prefect-worker: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect: + condition: service_healthy + environment: + - PREFECT_API_URL=http://prefect:4200/api + command: prefect worker start --pool local-pool + + prefect-postgres: + image: postgres:14-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=prefect + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=prefect + volumes: + - prefect-postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U prefect -d prefect"] + interval: 5s + timeout: 5s + retries: 5 + + prefect-redis: + image: redis:7.2-alpine + restart: unless-stopped + volumes: + - prefect-redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + prefect-postgres-data: + prefect-redis-data: diff --git a/blueprints/prefect/prefect.svg b/blueprints/prefect/prefect.svg new file mode 100644 index 00000000..07bf5b43 --- /dev/null +++ b/blueprints/prefect/prefect.svg @@ -0,0 +1,7 @@ + + Prefect + Prefect template icon + + + + diff --git a/blueprints/prefect/template.toml b/blueprints/prefect/template.toml new file mode 100644 index 00000000..15f166e5 --- /dev/null +++ b/blueprints/prefect/template.toml @@ -0,0 +1,15 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" + +[config] +env = [ + "PREFECT_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "prefect" +port = 4200 +host = "${main_domain}" diff --git a/meta.json b/meta.json index c8a04988..2e6eaefb 100644 --- a/meta.json +++ b/meta.json @@ -5689,6 +5689,23 @@ ], "dokploy_version": "<0.22.5" }, + { + "id": "prefect", + "name": "Prefect", + "version": "3.7.5-python3.12", + "description": "Prefect is an open-source workflow orchestration platform for building, scheduling, monitoring, and managing data pipelines and Python workflows.", + "logo": "prefect.svg", + "links": { + "github": "https://github.com/PrefectHQ/prefect", + "website": "https://www.prefect.io/", + "docs": "https://docs.prefect.io/" + }, + "tags": [ + "automation", + "workflow", + "data" + ] + }, { "id": "privatebin", "name": "PrivateBin", From 38483d467cf6250f55cb9b5d94de036906a0ca9a Mon Sep 17 00:00:00 2001 From: df4p <38404+df4p@users.noreply.github.com> Date: Tue, 7 Jul 2026 18:44:13 -0300 Subject: [PATCH 31/55] feat: Add OmniRoute - Free LLM Gateway Template (#920) * feat: Add OmniRoute - Free LLM Gateway Template Adding the OmniRoute template to Dokploy. Never stop coding. Free AI gateway: one endpoint, 160+ providers (50+ free), connect Claude Code, Codex, Cursor, Cline & Copilot to FREE Claude/GPT/Gemini. RTK+Caveman stacked compression saves 15-95% tokens, smart auto-fallback, MCP/A2A, multimodal APIs, Desktop/PWA. * fix: Testing files updated Testing files with pnpm updated. * fix(omniroute): pin OmniRoute image version, fix template.toml syntax, normalize metadata * chore: Add VS Code setting for git config auto-approval * chore(app): add pnpm-workspace.yaml to fix pnpm workspace config --- .vscode/settings.json | 5 +++ app/pnpm-workspace.yaml | 4 +++ blueprints/omniroute/docker-compose.yml | 40 ++++++++++++++++++++++++ blueprints/omniroute/omniroute.png | Bin 0 -> 7230 bytes blueprints/omniroute/template.toml | 31 ++++++++++++++++++ meta.json | 20 ++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 app/pnpm-workspace.yaml create mode 100644 blueprints/omniroute/docker-compose.yml create mode 100644 blueprints/omniroute/omniroute.png create mode 100644 blueprints/omniroute/template.toml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..50e5eba2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "chat.tools.terminal.autoApprove": { + "git config": true + } +} \ No newline at end of file diff --git a/app/pnpm-workspace.yaml b/app/pnpm-workspace.yaml new file mode 100644 index 00000000..c4ec45c0 --- /dev/null +++ b/app/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - . +allowBuilds: + esbuild: false diff --git a/blueprints/omniroute/docker-compose.yml b/blueprints/omniroute/docker-compose.yml new file mode 100644 index 00000000..552c8f26 --- /dev/null +++ b/blueprints/omniroute/docker-compose.yml @@ -0,0 +1,40 @@ +services: + omniroute: + image: diegosouzapw/omniroute:3.8.17 + restart: unless-stopped + + expose: + - "20128" + - "20129" + + volumes: + - omniroute-prod-data:/app/data + + environment: + - NODE_ENV=production + - PORT=${PORT:-20128} + - DASHBOARD_PORT=${DASHBOARD_PORT:-${PORT:-20128}} + - API_PORT=${API_PORT:-20129} + - API_HOST=${API_HOST:-0.0.0.0} + - HOSTNAME=0.0.0.0 + - DATA_DIR=/app/data + - PATH=/home/node/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + + redis: + image: redis:8.6.2-alpine + restart: unless-stopped + volumes: + - redis-prod-data:/data + command: redis-server --save 60 1 --loglevel warning + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + omniroute-prod-data: + + redis-prod-data: + + diff --git a/blueprints/omniroute/omniroute.png b/blueprints/omniroute/omniroute.png new file mode 100644 index 0000000000000000000000000000000000000000..4d4a0d1407f0f0db9dc10e3310d0c8893681cc5a GIT binary patch literal 7230 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 E0>SdMVE_OC literal 0 HcmV?d00001 diff --git a/blueprints/omniroute/template.toml b/blueprints/omniroute/template.toml new file mode 100644 index 00000000..ae4a61e0 --- /dev/null +++ b/blueprints/omniroute/template.toml @@ -0,0 +1,31 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "omniroute" +port = 20128 +host = "${main_domain}" + +[config.env] +JWT_SECRET = "${base64:64}" +API_KEY_SECRET = "${base64:64}" +INITIAL_PASSWORD = "${base64:64}" +STORAGE_ENCRYPTION_KEY = "${base64:64}" +STORAGE_ENCRYPTION_KEY_VERSION = "v1" +DISABLE_SQLITE_AUTO_BACKUP = false +PORT = 20128 +API_PORT = 20129 +OMNIROUTE_USE_TURBOPACK = 1 +NODE_ENV = "production" +MACHINE_ID_SALT = "endpoint-proxy-salt" +AUTH_COOKIE_SECURE = false +REQUIRE_API_KEY = false +ALLOW_API_KEY_REVEAL = false +BASE_URL = "http://localhost:20128" +CLOUD_URL = "" +NEXT_PUBLIC_BASE_URL = "http://localhost:20128" +ENABLE_SOCKS5_PROXY = true +NEXT_PUBLIC_ENABLE_SOCKS5_PROXY = true +PROVIDER_LIMITS_SYNC_INTERVAL_MINUTES = 70 +APP_LOG_TO_FILE = true diff --git a/meta.json b/meta.json index 2e6eaefb..0bb03d88 100644 --- a/meta.json +++ b/meta.json @@ -4931,6 +4931,26 @@ "self-hosted" ] }, + { + "id": "omniroute", + "name": "OmniRoute", + "version": "3.8.17", + "description": "The Free AI Gateway - Never stop coding. Connect every AI tool to 177 providers — 50+ free — through one endpoint. Auto-fallback. RTK + Caveman compression saves 15–95% tokens. Never hit limits. ~1.9B+ documented free tokens/month — up to ~2.5B in your first month with signup credits.", + "logo": "omniroute.png", + "links": { + "github": "https://github.com/diegosouzapw/OmniRoute", + "website": "https://github.com/diegosouzapw/OmniRoute", + "docs": "https://github.com/diegosouzapw/OmniRoute/wiki" + }, + "tags": [ + "artificial-intelligence", + "gateway", + "generative-ai", + "llm", + "monitoring", + "routing" + ] + }, { "id": "onedev", "name": "OneDev", From 438a079a6bbed7155d1e204a6685680712651d36 Mon Sep 17 00:00:00 2001 From: Magique <45526481+MagiqueDeveloper@users.noreply.github.com> Date: Wed, 8 Jul 2026 05:44:17 +0800 Subject: [PATCH 32/55] fix(discourse): replace deprecated Bitnami images with official alternatives (#910) * refactor(docker-compose): update PostgreSQL and Redis images, adjust environment variables * chore(meta.json): update Discourse version to 2026.1.4 * chore(docker-compose): comment out DISCOURSE_FORCE_HTTPS for production * fix(docker-compose): update Discourse app port to 80 for production --- blueprints/discourse/docker-compose.yml | 80 +++++++++---------------- blueprints/discourse/template.toml | 6 +- meta.json | 2 +- 3 files changed, 32 insertions(+), 56 deletions(-) diff --git a/blueprints/discourse/docker-compose.yml b/blueprints/discourse/docker-compose.yml index caafab5b..d0353e8a 100644 --- a/blueprints/discourse/docker-compose.yml +++ b/blueprints/discourse/docker-compose.yml @@ -1,29 +1,24 @@ -version: '3.7' - services: discourse-db: - image: docker.io/bitnami/postgresql:17 - + image: pgvector/pgvector:pg16 volumes: - - discourse-postgresql-data:/bitnami/postgresql + - discourse-postgresql-data:/var/lib/postgresql/data environment: - POSTGRESQL_USERNAME: bn_discourse - POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRESQL_DATABASE: bitnami_discourse + POSTGRES_USER: discourse + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: discourse healthcheck: - test: ["CMD-SHELL", "pg_isready -U bn_discourse -d bitnami_discourse"] + test: ["CMD-SHELL", "pg_isready -U discourse -d discourse"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped discourse-redis: - image: docker.io/bitnami/redis:7.4 - + image: redis:7-alpine volumes: - - discourse-redis-data:/bitnami/redis - environment: - REDIS_PASSWORD: ${REDIS_PASSWORD} + - discourse-redis-data:/data + command: redis-server --requirepass ${REDIS_PASSWORD} healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s @@ -32,59 +27,38 @@ services: restart: unless-stopped discourse-app: - image: docker.io/bitnamilegacy/discourse:3.5.0 - + image: discourse/discourse:2026.1.4 volumes: - - discourse-data:/bitnami/discourse + - discourse-data:/shared depends_on: discourse-db: condition: service_healthy discourse-redis: condition: service_healthy environment: - DISCOURSE_HOST: ${DISCOURSE_HOST} - DISCOURSE_DATABASE_HOST: discourse-db - DISCOURSE_DATABASE_PORT_NUMBER: 5432 - DISCOURSE_DATABASE_USER: bn_discourse - DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD} - DISCOURSE_DATABASE_NAME: bitnami_discourse + DISCOURSE_HOSTNAME: ${DISCOURSE_HOST} + DISCOURSE_DB_HOST: discourse-db + DISCOURSE_DB_PORT: 5432 + DISCOURSE_DB_USERNAME: discourse + DISCOURSE_DB_PASSWORD: ${POSTGRES_PASSWORD} + DISCOURSE_DB_NAME: discourse DISCOURSE_REDIS_HOST: discourse-redis - DISCOURSE_REDIS_PORT_NUMBER: 6379 + DISCOURSE_REDIS_PORT: 6379 DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD} - # Optional: Configure SMTP for email delivery - # DISCOURSE_SMTP_HOST: ${SMTP_HOST} + DISCOURSE_DEVELOPER_EMAILS: ${DISCOURSE_ADMIN_EMAIL} + # Required for email delivery - Discourse will not function correctly without SMTP + # DISCOURSE_SMTP_ADDRESS: ${SMTP_HOST} # DISCOURSE_SMTP_PORT: ${SMTP_PORT} - # DISCOURSE_SMTP_USER: ${SMTP_USER} - # DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD} - restart: unless-stopped - - discourse-sidekiq: - image: docker.io/bitnamilegacy/discourse:3.5.0 - - volumes: - - discourse-sidekiq-data:/bitnami/discourse - depends_on: - - discourse-app - command: /opt/bitnami/scripts/discourse-sidekiq/run.sh - environment: - DISCOURSE_HOST: ${DISCOURSE_HOST} - DISCOURSE_DATABASE_HOST: discourse-db - DISCOURSE_DATABASE_PORT_NUMBER: 5432 - DISCOURSE_DATABASE_USER: bn_discourse - DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD} - DISCOURSE_DATABASE_NAME: bitnami_discourse - DISCOURSE_REDIS_HOST: discourse-redis - DISCOURSE_REDIS_PORT_NUMBER: 6379 - DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD} - # Optional: Configure SMTP for email delivery - # DISCOURSE_SMTP_HOST: ${SMTP_HOST} - # DISCOURSE_SMTP_PORT: ${SMTP_PORT} - # DISCOURSE_SMTP_USER: ${SMTP_USER} + # DISCOURSE_SMTP_USER_NAME: ${SMTP_USER} # DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD} + # DISCOURSE_SMTP_ENABLE_START_TLS: "true" + # DISCOURSE_NOTIFICATION_EMAIL: [email protected] + # DISCOURSE_DEVELOPER_EMAILS: [email protected] + ports: + - 80 restart: unless-stopped volumes: discourse-postgresql-data: discourse-redis-data: discourse-data: - discourse-sidekiq-data: \ No newline at end of file diff --git a/blueprints/discourse/template.toml b/blueprints/discourse/template.toml index c02aa2c7..10358839 100644 --- a/blueprints/discourse/template.toml +++ b/blueprints/discourse/template.toml @@ -1,11 +1,13 @@ [variables] main_domain = "${domain}" +admin_email = "${email}" postgres_password = "${password}" redis_password = "${password}" [config] env = [ "DISCOURSE_HOST=${main_domain}", + "DISCOURSE_ADMIN_EMAIL=${admin_email}", "POSTGRES_PASSWORD=${postgres_password}", "REDIS_PASSWORD=${redis_password}", ] @@ -13,5 +15,5 @@ mounts = [] [[config.domains]] serviceName = "discourse-app" -port = 3_000 -host = "${main_domain}" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/meta.json b/meta.json index 0bb03d88..9e132a03 100644 --- a/meta.json +++ b/meta.json @@ -1871,7 +1871,7 @@ { "id": "discourse", "name": "Discourse", - "version": "3.5.0", + "version": "2026.1.4", "description": "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", "logo": "discourse.svg", "links": { From 599588d095726d3f08b163bdf63ac04f67bb19d8 Mon Sep 17 00:00:00 2001 From: rjnevins Date: Tue, 7 Jul 2026 17:44:20 -0400 Subject: [PATCH 33/55] feat: add Taiga template (#895) Adds a Dokploy template for Taiga (taigaio/taiga-back, taiga-front, taiga-events, taiga-protected) wired through an nginx gateway, with RabbitMQ and Postgres backing services. Diverges from prior PR #547 in two ways: - Strips trailing inline `# ...` comments from env values in template.toml. Those comments were captured as part of the value when Dokploy expanded them into docker-compose, polluting strings like TAIGA_SCHEME, EMAIL_BACKEND, etc. and breaking Django URL/email configuration at runtime. - Adds explicit SUBPATH= and RABBITMQ_VHOST env entries that PR #547 referenced but never set, so variable expansion no longer falls back to empty/undefined. Validated locally with build-scripts/validate-template.ts, build-scripts/validate-docker-compose.ts, and meta.json required-field + dedupe-and-sort checks. No runtime deploy was performed locally. Co-authored-by: Richard Nevins --- blueprints/taiga/docker-compose.yml | 145 ++++++++++++++++++++++++++++ blueprints/taiga/logo.png | Bin 0 -> 15955 bytes blueprints/taiga/template.toml | 114 ++++++++++++++++++++++ meta.json | 18 ++++ 4 files changed, 277 insertions(+) create mode 100644 blueprints/taiga/docker-compose.yml create mode 100644 blueprints/taiga/logo.png create mode 100644 blueprints/taiga/template.toml diff --git a/blueprints/taiga/docker-compose.yml b/blueprints/taiga/docker-compose.yml new file mode 100644 index 00000000..7649d283 --- /dev/null +++ b/blueprints/taiga/docker-compose.yml @@ -0,0 +1,145 @@ +version: "3.8" + +x-environment: &default-back-environment + POSTGRES_DB: taiga + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_HOST: taiga-db + TAIGA_SECRET_KEY: ${SECRET_KEY} + TAIGA_SITES_SCHEME: ${TAIGA_SCHEME} + TAIGA_SITES_DOMAIN: ${TAIGA_DOMAIN} + TAIGA_SUBPATH: ${SUBPATH} + EMAIL_BACKEND: django.core.mail.backends.${EMAIL_BACKEND}.EmailBackend + DEFAULT_FROM_EMAIL: ${EMAIL_DEFAULT_FROM} + EMAIL_USE_TLS: ${EMAIL_USE_TLS} + EMAIL_USE_SSL: ${EMAIL_USE_SSL} + EMAIL_HOST: ${EMAIL_HOST} + EMAIL_PORT: ${EMAIL_PORT} + EMAIL_HOST_USER: ${EMAIL_HOST_USER} + EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD} + RABBITMQ_USER: ${RABBITMQ_USER} + RABBITMQ_PASS: ${RABBITMQ_PASS} + RABBITMQ_VHOST: ${RABBITMQ_VHOST} + ENABLE_TELEMETRY: ${ENABLE_TELEMETRY} + PUBLIC_REGISTER_ENABLED: ${PUBLIC_REGISTER_ENABLED} + +x-back-volumes: &default-back-volumes + - taiga-static-data:/taiga-back/static + - taiga-media-data:/taiga-back/media + +services: + taiga-db: + image: postgres:12.3 + restart: unless-stopped + environment: + POSTGRES_DB: taiga + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 2s + timeout: 15s + retries: 5 + start_period: 3s + volumes: + - taiga-db-data:/var/lib/postgresql/data + + taiga-back: + image: taigaio/taiga-back:latest + restart: unless-stopped + environment: *default-back-environment + volumes: *default-back-volumes + depends_on: + taiga-db: + condition: service_healthy + taiga-events-rabbitmq: + condition: service_started + taiga-async-rabbitmq: + condition: service_started + + taiga-async: + image: taigaio/taiga-back:latest + restart: unless-stopped + entrypoint: ["/taiga-back/docker/async_entrypoint.sh"] + environment: *default-back-environment + volumes: *default-back-volumes + depends_on: + taiga-db: + condition: service_healthy + taiga-events-rabbitmq: + condition: service_started + taiga-async-rabbitmq: + condition: service_started + + taiga-async-rabbitmq: + image: rabbitmq:3.8-management-alpine + restart: unless-stopped + hostname: taiga-async-rabbitmq + environment: + RABBITMQ_ERLANG_COOKIE: ${RABBITMQ_ERLANG_COOKIE} + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS} + RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST} + volumes: + - taiga-async-rabbitmq-data:/var/lib/rabbitmq + + taiga-front: + image: taigaio/taiga-front:latest + restart: unless-stopped + environment: + TAIGA_URL: ${TAIGA_SCHEME}://${TAIGA_DOMAIN} + TAIGA_WEBSOCKETS_URL: ${WEBSOCKETS_SCHEME}://${TAIGA_DOMAIN} + TAIGA_SUBPATH: ${SUBPATH} + PUBLIC_REGISTER_ENABLED: ${PUBLIC_REGISTER_ENABLED_FRONT} + + taiga-events: + image: taigaio/taiga-events:latest + restart: unless-stopped + environment: + RABBITMQ_USER: ${RABBITMQ_USER} + RABBITMQ_PASS: ${RABBITMQ_PASS} + RABBITMQ_VHOST: ${RABBITMQ_VHOST} + TAIGA_SECRET_KEY: ${SECRET_KEY} + depends_on: + taiga-events-rabbitmq: + condition: service_started + + taiga-events-rabbitmq: + image: rabbitmq:3.8-management-alpine + restart: unless-stopped + hostname: taiga-events-rabbitmq + environment: + RABBITMQ_ERLANG_COOKIE: ${RABBITMQ_ERLANG_COOKIE} + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS} + RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST} + volumes: + - taiga-events-rabbitmq-data:/var/lib/rabbitmq + + taiga-protected: + image: taigaio/taiga-protected:latest + restart: unless-stopped + environment: + MAX_AGE: ${ATTACHMENTS_MAX_AGE} + SECRET_KEY: ${SECRET_KEY} + + taiga-gateway: + image: nginx:1.19-alpine + restart: unless-stopped + expose: + - 80 + volumes: + - ../files/volumes/nginx/taiga.conf:/etc/nginx/conf.d/default.conf:ro + - taiga-static-data:/taiga/static:ro + - taiga-media-data:/taiga/media:ro + depends_on: + - taiga-front + - taiga-back + - taiga-events + +volumes: + taiga-static-data: + taiga-media-data: + taiga-db-data: + taiga-async-rabbitmq-data: + taiga-events-rabbitmq-data: diff --git a/blueprints/taiga/logo.png b/blueprints/taiga/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..064c83ef27f2fe493b7eacd3230fbe73012db41a GIT binary patch literal 15955 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 Bx9I=? literal 0 HcmV?d00001 diff --git a/blueprints/taiga/template.toml b/blueprints/taiga/template.toml new file mode 100644 index 00000000..c528fd32 --- /dev/null +++ b/blueprints/taiga/template.toml @@ -0,0 +1,114 @@ +[variables] +main_domain = "${domain}" +postgres_user = "taiga" +postgres_password = "${password:32}" +secret_key = "${password:64}" +rabbitmq_user = "taiga" +rabbitmq_password = "${password:32}" +rabbitmq_erlang_cookie = "${password:32}" + +[config] +env = [ + "TAIGA_SCHEME=https", + "TAIGA_DOMAIN=${main_domain}", + "WEBSOCKETS_SCHEME=wss", + "SUBPATH=", + "SECRET_KEY=${secret_key}", + "POSTGRES_USER=${postgres_user}", + "POSTGRES_PASSWORD=${postgres_password}", + "EMAIL_BACKEND=console", + "EMAIL_HOST=smtp.example.com", + "EMAIL_PORT=587", + "EMAIL_HOST_USER=user", + "EMAIL_HOST_PASSWORD=password", + "EMAIL_DEFAULT_FROM=changeme@example.com", + "EMAIL_USE_TLS=False", + "EMAIL_USE_SSL=False", + "RABBITMQ_USER=${rabbitmq_user}", + "RABBITMQ_PASS=${rabbitmq_password}", + "RABBITMQ_VHOST=taiga", + "RABBITMQ_ERLANG_COOKIE=${rabbitmq_erlang_cookie}", + "ATTACHMENTS_MAX_AGE=360", + "ENABLE_TELEMETRY=False", + "PUBLIC_REGISTER_ENABLED=False", + "PUBLIC_REGISTER_ENABLED_FRONT=false", +] + +[[config.domains]] +serviceName = "taiga-gateway" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "/volumes/nginx/taiga.conf" +content = """ +server { + listen 80 default_server; + + client_max_body_size 100M; + charset utf-8; + + location / { + proxy_pass http://taiga-front/; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + } + + location /api/ { + proxy_pass http://taiga-back:8000/api/; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + } + + location /admin/ { + proxy_pass http://taiga-back:8000/admin/; + proxy_pass_header Server; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + } + + location /static/ { + alias /taiga/static/; + } + + location /_protected/ { + internal; + alias /taiga/media/; + add_header Content-disposition "attachment"; + } + + location /media/exports/ { + alias /taiga/media/exports/; + add_header Content-disposition "attachment"; + } + + location /media/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://taiga-protected:8003/; + proxy_redirect off; + } + + location /events { + proxy_pass http://taiga-events:8888/events; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_connect_timeout 7d; + proxy_send_timeout 7d; + proxy_read_timeout 7d; + } +} +""" diff --git a/meta.json b/meta.json index 9e132a03..80889bb6 100644 --- a/meta.json +++ b/meta.json @@ -6654,6 +6654,24 @@ "backup" ] }, + { + "id": "taiga", + "name": "Taiga", + "version": "latest", + "description": "Taiga is a free and open-source project management platform for agile developers, designers and project managers.", + "logo": "logo.png", + "links": { + "github": "https://github.com/taigaio/taiga-docker", + "website": "https://taiga.io/", + "docs": "https://docs.taiga.io/" + }, + "tags": [ + "project-management", + "agile", + "scrum", + "kanban" + ] + }, { "id": "tailscale-exitnode", "name": "Tailscale Exit nodes", From d1cc00391e1e7bb0c1ade207fe5904a8f3b33c90 Mon Sep 17 00:00:00 2001 From: autonomousventurelab-jpg Date: Tue, 7 Jul 2026 14:44:23 -0700 Subject: [PATCH 34/55] feat: add HedgeDoc template (#893) Co-authored-by: Mauricio Siu --- blueprints/hedgedoc/docker-compose.yml | 37 ++++++++++++++++++++++++++ blueprints/hedgedoc/hedgedoc.svg | 32 ++++++++++++++++++++++ blueprints/hedgedoc/template.toml | 16 +++++++++++ meta.json | 19 +++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 blueprints/hedgedoc/docker-compose.yml create mode 100644 blueprints/hedgedoc/hedgedoc.svg create mode 100644 blueprints/hedgedoc/template.toml diff --git a/blueprints/hedgedoc/docker-compose.yml b/blueprints/hedgedoc/docker-compose.yml new file mode 100644 index 00000000..8ee2e567 --- /dev/null +++ b/blueprints/hedgedoc/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.8" + +services: + hedgedoc: + image: quay.io/hedgedoc/hedgedoc:1.10.8 + restart: unless-stopped + depends_on: + database: + condition: service_healthy + environment: + - CMD_DB_URL=${CMD_DB_URL} + - CMD_DOMAIN=${CMD_DOMAIN} + - CMD_PROTOCOL_USESSL=${CMD_PROTOCOL_USESSL} + - CMD_URL_ADDPORT=${CMD_URL_ADDPORT} + volumes: + - hedgedoc_uploads:/hedgedoc/public/uploads + expose: + - "3000" + + database: + image: postgres:17.7-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=hedgedoc + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=hedgedoc + volumes: + - hedgedoc_database:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U hedgedoc -d hedgedoc"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + hedgedoc_uploads: + hedgedoc_database: diff --git a/blueprints/hedgedoc/hedgedoc.svg b/blueprints/hedgedoc/hedgedoc.svg new file mode 100644 index 00000000..0d236216 --- /dev/null +++ b/blueprints/hedgedoc/hedgedoc.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/hedgedoc/template.toml b/blueprints/hedgedoc/template.toml new file mode 100644 index 00000000..8e82e9d5 --- /dev/null +++ b/blueprints/hedgedoc/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +database_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "hedgedoc" +port = 3000 +host = "${main_domain}" + +[config.env] +POSTGRES_PASSWORD = "${database_password}" +CMD_DB_URL = "postgres://hedgedoc:${database_password}@database:5432/hedgedoc" +CMD_DOMAIN = "${main_domain}" +CMD_PROTOCOL_USESSL = "true" +CMD_URL_ADDPORT = "false" diff --git a/meta.json b/meta.json index 80889bb6..f20f46d6 100644 --- a/meta.json +++ b/meta.json @@ -3106,6 +3106,25 @@ "self-hosted" ] }, + { + "id": "hedgedoc", + "name": "HedgeDoc", + "version": "1.10.8", + "description": "HedgeDoc is a collaborative Markdown editor for teams that need real-time notes, documentation, and knowledge sharing.", + "logo": "hedgedoc.svg", + "links": { + "github": "https://github.com/hedgedoc/hedgedoc", + "website": "https://hedgedoc.org/", + "docs": "https://docs.hedgedoc.org/" + }, + "tags": [ + "markdown", + "notes", + "documentation", + "collaboration", + "wiki" + ] + }, { "id": "hermes", "name": "Hermes", From 3d74ad7c086e1d79153b95311d28c634239db676 Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:44:27 +0200 Subject: [PATCH 35/55] feat: add Soft Serve template (#889) * feat: add Soft Serve template * fix: quote Soft Serve listen addresses --------- Co-authored-by: Mauricio Siu --- blueprints/soft-serve/docker-compose.yml | 27 +++++++++++ blueprints/soft-serve/soft-serve.svg | 10 ++++ blueprints/soft-serve/template.toml | 62 ++++++++++++++++++++++++ meta.json | 18 +++++++ 4 files changed, 117 insertions(+) create mode 100644 blueprints/soft-serve/docker-compose.yml create mode 100644 blueprints/soft-serve/soft-serve.svg create mode 100644 blueprints/soft-serve/template.toml diff --git a/blueprints/soft-serve/docker-compose.yml b/blueprints/soft-serve/docker-compose.yml new file mode 100644 index 00000000..ac875d21 --- /dev/null +++ b/blueprints/soft-serve/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.8" + +services: + soft-serve: + image: charmcli/soft-serve:v0.11.6 + restart: unless-stopped + expose: + - "23231" + - "23232" + - "23233" + - "9418" + environment: + SOFT_SERVE_NAME: ${SOFT_SERVE_NAME} + SOFT_SERVE_DATA_PATH: /soft-serve + SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS} + SOFT_SERVE_SSH_LISTEN_ADDR: ":23231" + SOFT_SERVE_SSH_PUBLIC_URL: ${SOFT_SERVE_SSH_PUBLIC_URL} + SOFT_SERVE_HTTP_LISTEN_ADDR: ":23232" + SOFT_SERVE_HTTP_PUBLIC_URL: ${SOFT_SERVE_HTTP_PUBLIC_URL} + SOFT_SERVE_GIT_LISTEN_ADDR: ":9418" + SOFT_SERVE_STATS_LISTEN_ADDR: ":23233" + SOFT_SERVE_LOG_LEVEL: ${SOFT_SERVE_LOG_LEVEL} + volumes: + - soft-serve-data:/soft-serve + +volumes: + soft-serve-data: diff --git a/blueprints/soft-serve/soft-serve.svg b/blueprints/soft-serve/soft-serve.svg new file mode 100644 index 00000000..0bb41d55 --- /dev/null +++ b/blueprints/soft-serve/soft-serve.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/blueprints/soft-serve/template.toml b/blueprints/soft-serve/template.toml new file mode 100644 index 00000000..f18f883c --- /dev/null +++ b/blueprints/soft-serve/template.toml @@ -0,0 +1,62 @@ +[variables] +main_domain = "${domain}" +server_name = "Soft Serve" +initial_admin_keys = "" +http_public_url = "http://${main_domain}" +ssh_public_url = "ssh://${main_domain}:23231" +log_level = "info" + +[[config.domains]] +serviceName = "soft-serve" +port = 23232 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Soft Serve + +This template deploys Soft Serve, a self-hosted Git server with an SSH TUI, HTTP Git access, Git LFS support, and a lightweight web endpoint. + +## First Start + +Set `initial_admin_keys` to one or more SSH public keys before the first deployment. Soft Serve uses this value to create the initial `admin` user. The keys are consumed during initialization, so changing this variable later does not rewrite an already initialized data volume. + +Example value: + +```text +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... your-name +``` + +## HTTP Access + +The Dokploy domain routes to Soft Serve's HTTP listener: + +- HTTP public URL: `http://${main_domain}` +- HTTP Git clone example: `git clone http://${main_domain}/repo.git` + +Set `http_public_url` to `https://${main_domain}` before deploying if your Dokploy domain enforces TLS. + +Private HTTP access uses tokens created through the SSH admin interface. + +## SSH and Git Protocol Ports + +Soft Serve also listens inside the deployment on: + +- SSH/TUI: `23231` +- Stats: `23233` +- Git protocol: `9418` + +This template does not publish host TCP ports directly. Expose the SSH or Git protocol ports separately in Dokploy if you need direct terminal/TUI access, then use the configured `ssh_public_url` for clone commands. + +## Storage + +Repositories, configuration, keys, and the default SQLite database are stored in the persistent `soft-serve-data` volume mounted at `/soft-serve`. +""" + +[config.env] +SOFT_SERVE_NAME = "${server_name}" +SOFT_SERVE_INITIAL_ADMIN_KEYS = "${initial_admin_keys}" +SOFT_SERVE_HTTP_PUBLIC_URL = "${http_public_url}" +SOFT_SERVE_SSH_PUBLIC_URL = "${ssh_public_url}" +SOFT_SERVE_LOG_LEVEL = "${log_level}" diff --git a/meta.json b/meta.json index f20f46d6..73391677 100644 --- a/meta.json +++ b/meta.json @@ -6410,6 +6410,24 @@ "self-hosted" ] }, + { + "id": "soft-serve", + "name": "Soft Serve", + "version": "v0.11.6", + "description": "Soft Serve is a self-hosted Git server with an SSH TUI, HTTP Git access, Git LFS support, and simple repository management.", + "logo": "soft-serve.svg", + "links": { + "github": "https://github.com/charmbracelet/soft-serve", + "website": "https://charm.sh/", + "docs": "https://github.com/charmbracelet/soft-serve/blob/main/README.md" + }, + "tags": [ + "git", + "ssh", + "developer-tools", + "self-hosted" + ] + }, { "id": "soketi", "name": "Soketi", From 3829b188d842d1da6bc5e175fb1e5175e49eac23 Mon Sep 17 00:00:00 2001 From: Juan del Peral Date: Tue, 7 Jul 2026 18:44:30 -0300 Subject: [PATCH 36/55] feat(meta): add Arche AI workspace platform to meta.json (#875) Introduce Arche, a self-hosted AI workspace platform, with relevant metadata including description, logo, and GitHub link. This addition enhances the diversity of tools available in the project. Co-authored-by: Mauricio Siu --- blueprints/arche/arche.svg | 1 + blueprints/arche/docker-compose.yml | 102 ++++++++++++++++++++++++++++ blueprints/arche/template.toml | 33 +++++++++ build-scripts/pnpm-workspace.yaml | 2 + meta.json | 17 +++++ 5 files changed, 155 insertions(+) create mode 100644 blueprints/arche/arche.svg create mode 100644 blueprints/arche/docker-compose.yml create mode 100644 blueprints/arche/template.toml create mode 100644 build-scripts/pnpm-workspace.yaml diff --git a/blueprints/arche/arche.svg b/blueprints/arche/arche.svg new file mode 100644 index 00000000..06ae9720 --- /dev/null +++ b/blueprints/arche/arche.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/arche/docker-compose.yml b/blueprints/arche/docker-compose.yml new file mode 100644 index 00000000..cbefbe7e --- /dev/null +++ b/blueprints/arche/docker-compose.yml @@ -0,0 +1,102 @@ +version: "3.8" + +# Arche — AI workspace platform that spawns per-user OpenCode containers +# via a Docker socket proxy. +# +# PRE-REQUISITES (run once on the VPS before deploying): +# mkdir -p /opt/arche/kb-content /opt/arche/kb-config /opt/arche/users +# +# Optional (only needed to seed the Knowledge Base): +# git init --bare --initial-branch=main /opt/arche/kb-content +# git init --bare --initial-branch=main /opt/arche/kb-config +# +# POST-DEPLOY (set in Dokploy → Environment, then redeploy): +# 1. Deploy this template once. Dokploy auto-creates a network for the +# compose stack. +# 2. SSH into the host and find the network name: +# docker network ls --format '{{.Name}}' | grep arche +# It looks like `_default` or similar. +# 3. Add the env var OPENCODE_NETWORK= and redeploy. +# Without it, spawned user workspaces will fail to start. +# +# IMAGE ARCHITECTURE: +# Default tags target linux/amd64. For ARM64 hosts (Apple Silicon, +# Ampere, Graviton), override both images to the `-arm64` variants in +# Dokploy → Environment: +# ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest-arm64 +# ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest-arm64 + +services: + web: + image: ${ARCHE_WEB_IMAGE:-ghcr.io/peaberry-studio/arche/web:latest} + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + docker-socket-proxy: + condition: service_started + environment: + ARCHE_DOMAIN: ${ARCHE_DOMAIN} + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/arche?schema=public + + ARCHE_SESSION_PEPPER: ${ARCHE_SESSION_PEPPER} + ARCHE_ENCRYPTION_KEY: ${ARCHE_ENCRYPTION_KEY} + ARCHE_INTERNAL_TOKEN: ${ARCHE_INTERNAL_TOKEN} + ARCHE_GATEWAY_TOKEN_SECRET: ${ARCHE_GATEWAY_TOKEN_SECRET} + ARCHE_CONNECTOR_OAUTH_STATE_SECRET: ${ARCHE_CONNECTOR_OAUTH_STATE_SECRET} + + ARCHE_GATEWAY_TOKEN_TTL_SECONDS: "86400" + ARCHE_GATEWAY_BASE_URL: http://web:3000 + ARCHE_PUBLIC_BASE_URL: https://${ARCHE_DOMAIN} + ARCHE_SESSION_TTL_DAYS: "7" + ARCHE_COOKIE_SECURE: "true" + + ARCHE_SEED_ADMIN_EMAIL: ${ARCHE_SEED_ADMIN_EMAIL} + ARCHE_SEED_ADMIN_PASSWORD: ${ARCHE_SEED_ADMIN_PASSWORD} + ARCHE_SEED_ADMIN_SLUG: ${ARCHE_SEED_ADMIN_SLUG:-admin} + + CONTAINER_PROXY_HOST: docker-socket-proxy + CONTAINER_PROXY_PORT: "2375" + OPENCODE_IMAGE: ${ARCHE_WORKSPACE_IMAGE:-ghcr.io/peaberry-studio/arche/workspace:latest} + OPENCODE_NETWORK: ${OPENCODE_NETWORK} + + KB_CONTENT_HOST_PATH: /opt/arche/kb-content + KB_CONFIG_HOST_PATH: /opt/arche/kb-config + ARCHE_USERS_PATH: /opt/arche/users + expose: + - "3000" + volumes: + - /opt/arche/kb-content:/kb-content + - /opt/arche/kb-config:/kb-config + - /opt/arche/users:/opt/arche/users + + postgres: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: arche + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + docker-socket-proxy: + image: ghcr.io/tecnativa/docker-socket-proxy:master + restart: unless-stopped + environment: + CONTAINERS: 1 + NETWORKS: 1 + IMAGES: 1 + INFO: 1 + POST: 1 + VOLUMES: 1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + postgres_data: diff --git a/blueprints/arche/template.toml b/blueprints/arche/template.toml new file mode 100644 index 00000000..5772c7b3 --- /dev/null +++ b/blueprints/arche/template.toml @@ -0,0 +1,33 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +session_pepper = "${base64:32}" +encryption_key = "${base64:32}" +internal_token = "${base64:32}" +gateway_token_secret = "${base64:32}" +connector_oauth_state_secret = "${base64:32}" +seed_admin_email = "${email}" +seed_admin_password = "${password:16}" + +[config] +env = [ + "ARCHE_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", + "ARCHE_SESSION_PEPPER=${session_pepper}", + "ARCHE_ENCRYPTION_KEY=${encryption_key}", + "ARCHE_INTERNAL_TOKEN=${internal_token}", + "ARCHE_GATEWAY_TOKEN_SECRET=${gateway_token_secret}", + "ARCHE_CONNECTOR_OAUTH_STATE_SECRET=${connector_oauth_state_secret}", + "ARCHE_SEED_ADMIN_EMAIL=${seed_admin_email}", + "ARCHE_SEED_ADMIN_PASSWORD=${seed_admin_password}", + "ARCHE_SEED_ADMIN_SLUG=admin", + "ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest", + "ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest", + "OPENCODE_NETWORK=", +] +mounts = [] + +[[config.domains]] +serviceName = "web" +port = 3000 +host = "${main_domain}" diff --git a/build-scripts/pnpm-workspace.yaml b/build-scripts/pnpm-workspace.yaml new file mode 100644 index 00000000..00f6fc47 --- /dev/null +++ b/build-scripts/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: set this to true or false diff --git a/meta.json b/meta.json index 73391677..2022bca3 100644 --- a/meta.json +++ b/meta.json @@ -449,6 +449,23 @@ "nosql" ] }, + { + "id": "arche", + "name": "Arche", + "version": "latest", + "description": "Arche is a self-hosted AI workspace platform that spawns per-user OpenCode coding containers backed by a shared Knowledge Base. Requires the `arche-internal` external Docker network and `/opt/arche/{kb-content,kb-config,users}` directories on the host before deploy. Images are linux/amd64 by default; override `ARCHE_WEB_IMAGE` and `ARCHE_WORKSPACE_IMAGE` to the `-arm64` tag variants for ARM64 hosts.", + "logo": "arche.svg", + "links": { + "github": "https://github.com/peaberry-studio/arche" + }, + "tags": [ + "ai", + "coding", + "workspace", + "self-hosted", + "llm" + ] + }, { "id": "archivebox", "name": "ArchiveBox", From 641fd75bf72689cbcefe282054a32f7ee8de1cd7 Mon Sep 17 00:00:00 2001 From: Abian Suarez <33789675+AbianS@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:44:33 +0200 Subject: [PATCH 37/55] feat: add rustrak and rustrak-full templates (#866) * feat: add rustrak and rustrak-full templates - rustrak: server-only with SQLite, single service, no external DB - rustrak-full: server + Next.js dashboard + PostgreSQL Both use abians7/rustrak-server:v0.2.1, Sentry-compatible error tracking * chore: update rustrak logo to new design --- blueprints/rustrak-full/docker-compose.yml | 49 ++++++++++++++++++++++ blueprints/rustrak-full/rustrak.svg | 6 +++ blueprints/rustrak-full/template.toml | 27 ++++++++++++ blueprints/rustrak/docker-compose.yml | 20 +++++++++ blueprints/rustrak/rustrak.svg | 6 +++ blueprints/rustrak/template.toml | 17 ++++++++ meta.json | 39 +++++++++++++++++ 7 files changed, 164 insertions(+) create mode 100644 blueprints/rustrak-full/docker-compose.yml create mode 100644 blueprints/rustrak-full/rustrak.svg create mode 100644 blueprints/rustrak-full/template.toml create mode 100644 blueprints/rustrak/docker-compose.yml create mode 100644 blueprints/rustrak/rustrak.svg create mode 100644 blueprints/rustrak/template.toml diff --git a/blueprints/rustrak-full/docker-compose.yml b/blueprints/rustrak-full/docker-compose.yml new file mode 100644 index 00000000..16f734c7 --- /dev/null +++ b/blueprints/rustrak-full/docker-compose.yml @@ -0,0 +1,49 @@ +version: "3.8" + +services: + rustrak-server: + image: abians7/rustrak-server:v0.2.1-postgres + restart: unless-stopped + environment: + - HOST=0.0.0.0 + - PORT=8080 + - RUST_LOG=info + - SSL_PROXY=true + - DATABASE_URL=postgres://rustrak:${POSTGRES_PASSWORD}@postgres:5432/rustrak + - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} + - CREATE_SUPERUSER=${CREATE_SUPERUSER} + ports: + - 8080 + depends_on: + postgres: + condition: service_healthy + + rustrak-ui: + image: abians7/rustrak-ui:v0.1.6 + restart: unless-stopped + environment: + - RUSTRAK_API_URL=${RUSTRAK_API_URL} + ports: + - 3000 + depends_on: + - rustrak-server + + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=rustrak + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=rustrak + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U rustrak -d rustrak"] + interval: 10s + timeout: 5s + retries: 5 + expose: + - 5432 + +volumes: + postgres_data: diff --git a/blueprints/rustrak-full/rustrak.svg b/blueprints/rustrak-full/rustrak.svg new file mode 100644 index 00000000..11d87e47 --- /dev/null +++ b/blueprints/rustrak-full/rustrak.svg @@ -0,0 +1,6 @@ + + + + diff --git a/blueprints/rustrak-full/template.toml b/blueprints/rustrak-full/template.toml new file mode 100644 index 00000000..383f486f --- /dev/null +++ b/blueprints/rustrak-full/template.toml @@ -0,0 +1,27 @@ +[variables] +main_domain = "${domain}" +api_domain = "${domain}" +postgres_password = "${password:32}" +session_secret_key = "${password:64}" +admin_email = "${email}" +admin_password = "${password:16}" +create_superuser = "${admin_email}:${admin_password}" +rustrak_api_url = "https://${api_domain}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "SESSION_SECRET_KEY=${session_secret_key}", + "CREATE_SUPERUSER=${create_superuser}", + "RUSTRAK_API_URL=${rustrak_api_url}", +] + +[[config.domains]] +serviceName = "rustrak-ui" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "rustrak-server" +port = 8080 +host = "${api_domain}" diff --git a/blueprints/rustrak/docker-compose.yml b/blueprints/rustrak/docker-compose.yml new file mode 100644 index 00000000..e4c52b0f --- /dev/null +++ b/blueprints/rustrak/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + rustrak: + image: abians7/rustrak-server:v0.2.1 + restart: unless-stopped + environment: + - HOST=0.0.0.0 + - PORT=8080 + - RUST_LOG=info + - SSL_PROXY=true + - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} + - CREATE_SUPERUSER=${CREATE_SUPERUSER} + volumes: + - rustrak_data:/data + ports: + - 8080 + +volumes: + rustrak_data: diff --git a/blueprints/rustrak/rustrak.svg b/blueprints/rustrak/rustrak.svg new file mode 100644 index 00000000..11d87e47 --- /dev/null +++ b/blueprints/rustrak/rustrak.svg @@ -0,0 +1,6 @@ + + + + diff --git a/blueprints/rustrak/template.toml b/blueprints/rustrak/template.toml new file mode 100644 index 00000000..819f6f85 --- /dev/null +++ b/blueprints/rustrak/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +session_secret_key = "${password:64}" +admin_email = "${email}" +admin_password = "${password:16}" +create_superuser = "${admin_email}:${admin_password}" + +[config] +env = [ + "SESSION_SECRET_KEY=${session_secret_key}", + "CREATE_SUPERUSER=${create_superuser}", +] + +[[config.domains]] +serviceName = "rustrak" +port = 8080 +host = "${main_domain}" diff --git a/meta.json b/meta.json index 2022bca3..653a7985 100644 --- a/meta.json +++ b/meta.json @@ -6127,6 +6127,45 @@ "rust" ] }, + { + "id": "rustrak", + "name": "Rustrak", + "version": "0.2.1", + "description": "Self-hosted error tracking compatible with Sentry SDKs. Server only, written in Rust with <100MB memory footprint and SQLite storage. No external database required.", + "logo": "rustrak.svg", + "links": { + "github": "https://github.com/AbianS/rustrak", + "website": "https://abians.github.io/rustrak/", + "docs": "https://abians.github.io/rustrak/" + }, + "tags": [ + "monitoring", + "error-tracking", + "sentry", + "rust", + "observability" + ] + }, + { + "id": "rustrak-full", + "name": "Rustrak (Full Stack)", + "version": "0.2.1", + "description": "Self-hosted error tracking compatible with Sentry SDKs. Includes server, web dashboard UI, and PostgreSQL. Written in Rust with <100MB memory footprint.", + "logo": "rustrak.svg", + "links": { + "github": "https://github.com/AbianS/rustrak", + "website": "https://abians.github.io/rustrak/", + "docs": "https://abians.github.io/rustrak/" + }, + "tags": [ + "monitoring", + "error-tracking", + "sentry", + "rust", + "observability", + "postgresql" + ] + }, { "id": "rutorrent", "name": "ruTorrent", From 31bff09eaaf3f0ca134579013f3591c249d7f843 Mon Sep 17 00:00:00 2001 From: luolink <97769040+abel-yelin@users.noreply.github.com> Date: Wed, 8 Jul 2026 05:44:36 +0800 Subject: [PATCH 38/55] Add Marketing Dashboard template (#861) --- .../marketing-dashboard/docker-compose.yml | 47 +++++++++++++++++++ blueprints/marketing-dashboard/logo.svg | 8 ++++ blueprints/marketing-dashboard/template.toml | 30 ++++++++++++ meta.json | 19 ++++++++ 4 files changed, 104 insertions(+) create mode 100644 blueprints/marketing-dashboard/docker-compose.yml create mode 100644 blueprints/marketing-dashboard/logo.svg create mode 100644 blueprints/marketing-dashboard/template.toml diff --git a/blueprints/marketing-dashboard/docker-compose.yml b/blueprints/marketing-dashboard/docker-compose.yml new file mode 100644 index 00000000..637849e7 --- /dev/null +++ b/blueprints/marketing-dashboard/docker-compose.yml @@ -0,0 +1,47 @@ +services: + marketing-dashboard: + image: ghcr.io/abel-yelin/marketing-dashboard:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + DATABASE_URL: ${DATABASE_URL} + DIRECT_URL: ${DIRECT_URL} + MARKETING_APP_URL: ${MARKETING_APP_URL} + MARKETING_COLLECTOR_UPSTREAM_URL: ${MARKETING_COLLECTOR_UPSTREAM_URL} + MARKETING_SESSION_TIMEOUT_MINUTES: ${MARKETING_SESSION_TIMEOUT_MINUTES} + MARKETING_PROXY_TIMEOUT_MS: ${MARKETING_PROXY_TIMEOUT_MS} + MARKETING_INGEST_MAX_BODY_BYTES: ${MARKETING_INGEST_MAX_BODY_BYTES} + MARKETING_INGEST_IP_RATE_LIMIT: ${MARKETING_INGEST_IP_RATE_LIMIT} + MARKETING_INGEST_CLIENT_RATE_LIMIT: ${MARKETING_INGEST_CLIENT_RATE_LIMIT} + MARKETING_INGEST_RATE_LIMIT_WINDOW_MS: ${MARKETING_INGEST_RATE_LIMIT_WINDOW_MS} + MARKETING_ENABLE_PILOT_API: ${MARKETING_ENABLE_PILOT_API} + MARKETING_DASHBOARD_ADMIN_USER: ${MARKETING_DASHBOARD_ADMIN_USER} + MARKETING_DASHBOARD_ADMIN_PASSWORD: ${MARKETING_DASHBOARD_ADMIN_PASSWORD} + ports: + - 3000 + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/health > /dev/null || exit 1"] + interval: 15s + timeout: 5s + retries: 10 + start_period: 30s + + postgres: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - marketing-dashboard-postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + +volumes: + marketing-dashboard-postgres: diff --git a/blueprints/marketing-dashboard/logo.svg b/blueprints/marketing-dashboard/logo.svg new file mode 100644 index 00000000..07a4ae88 --- /dev/null +++ b/blueprints/marketing-dashboard/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/marketing-dashboard/template.toml b/blueprints/marketing-dashboard/template.toml new file mode 100644 index 00000000..531874ea --- /dev/null +++ b/blueprints/marketing-dashboard/template.toml @@ -0,0 +1,30 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +admin_password = "${password:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "marketing-dashboard" +port = 3000 +host = "${main_domain}" + +[config.env] +POSTGRES_DB = "marketing_dashboard" +POSTGRES_USER = "marketing_dashboard" +POSTGRES_PASSWORD = "${postgres_password}" +DATABASE_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public" +DIRECT_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public" +MARKETING_APP_URL = "https://${main_domain}" +MARKETING_COLLECTOR_UPSTREAM_URL = "https://${main_domain}/api/ingest/browser" +MARKETING_SESSION_TIMEOUT_MINUTES = "30" +MARKETING_PROXY_TIMEOUT_MS = "4000" +MARKETING_INGEST_MAX_BODY_BYTES = "65536" +MARKETING_INGEST_IP_RATE_LIMIT = "300" +MARKETING_INGEST_CLIENT_RATE_LIMIT = "1200" +MARKETING_INGEST_RATE_LIMIT_WINDOW_MS = "60000" +MARKETING_ENABLE_PILOT_API = "false" +MARKETING_DASHBOARD_ADMIN_USER = "admin" +MARKETING_DASHBOARD_ADMIN_PASSWORD = "${admin_password}" diff --git a/meta.json b/meta.json index 653a7985..6e2a3a34 100644 --- a/meta.json +++ b/meta.json @@ -4245,6 +4245,25 @@ "smtp" ] }, + { + "id": "marketing-dashboard", + "name": "Marketing Dashboard", + "version": "latest", + "description": "Self-hosted marketing operations dashboard for product-led growth, checkout analytics, paid media, SEO signals, and Stripe readiness.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/abel-yelin/marketing-dashboard", + "website": "https://github.com/abel-yelin/marketing-dashboard", + "docs": "https://github.com/abel-yelin/marketing-dashboard/blob/main/docs/production-deployment.md" + }, + "tags": [ + "analytics", + "marketing", + "self-hosted", + "postgresql", + "nextjs" + ] + }, { "id": "mattermost", "name": "Mattermost", From 7095691571ca4c3432dc9c2a1bda0c558f717ec2 Mon Sep 17 00:00:00 2001 From: Naterfute <106989409+naterfute@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:44:39 -0700 Subject: [PATCH 39/55] updated Pyrodactyl logo (#855) --- blueprints/pyrodactyl/pyrodactyl.png | Bin 4283 -> 1669 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/blueprints/pyrodactyl/pyrodactyl.png b/blueprints/pyrodactyl/pyrodactyl.png index 50c98fdd6cc5eddc02caab84dd2adb4d257bcbbc..81513193e0c3ef0fa738d9644ebce9837b52b656 100644 GIT binary patch literal 1669 zcmZ`(eLT~79RInMhgMnSdN7&@6WKfs&C~gX}!gU>am<*H5 z5xK&l5Jv7&Ce&1uhjn%90fkd(Gj_lF<6f`(qu1;6{k}h+_rvG=-}fw?wtLO0&8q+a zSVJL${xDX-Cs`W--(Ovku7d#+N%r3ZbLIfRJp%wrnBu+y05%Q)Cc^>1l@9P*?t)|l#DI_(W438f_^GYfSZaz{;N~*V#0xtS~_QTAl z0%7&D6a2K{lmC;dR;$Zu{i@;ZXvm)y0Ql9-42GdRFsEc?I8kiQdCyC;PvgStyN2jo zl&Vq%`!o*u>;{dd5b?S2paoI(khCUOY8#l?hb>{60bs=#1@sC${^ViF)P*z*ii^#< zW9{93X?A(AmEnZcM+fJRD4N-!ih-_Bev!7>vZ0jj}jPk?YSb`*_SE1&yzCQxHmg_PzTK)8q+<~;jC>Q}heOv$l^Yi^s_%vVlnh*VUeMlD_4O@l*~PlTu9F58(De}}SMZQ>I3njt zm=xc)otUKe!&&DwzP;L>XmHblNBm|)UG0Utx{tk3Zhdv1KeKypy=^wHB7EOMpc-Rj*P%b`l-$ zcz_d&Z^xH!X4Ftqv z`8B~kbo7jS34~-&H478pRz7bwIC;@p7VNT*m{-+Hv-W{eNcIRuD}mE{6(M z1w8`9e_atIwT&SIEOw%*YG_ICg1hw&BRy!PZ!mn4G-aoqc1?%Z>#>oOMH+5q2W@Y> zh-{jdH$ly7W-bW4F^psBz`)U9rJ0ez*2d=%Q;UxVqJ&LDCV583IAp_ZISFJm!pw!S zc5$g7>6FS{elNzd+|&no~iot z2a9AE_%Gug5X^M5~8Wxc4*`|;&ueZBKJsm_va->~wGjEiF}hHkLy*_;ZN zPuOBMS{7BVo4YU(LC*FM$<1p$pgt--TzBL3GR&;0W=2UO; zdq;du*UQW=5+!ZuyW=@wg&s^gw3_B#9Fj5+p|%QWUn~p#;~9Eo?_=r2kvE0$$y@<}@l7=XRKhOGQ=n;RQovrV5%q6(}(^9)l zCwpTfC-edK<@))HiQiWyom($derx^?ar^H`7mYvoZt&N)4I%|Y%m^sTH8LRz2EYzy z=VXI(w81$C+TmU8?Oh%5+i^Ho94?H&Hxk=4ZQYEc-p_74*AUh literal 4283 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 From 708b3019bf8bfdbbc99ab35fd0fd0c8d1b66523c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ya=C4=9F=C4=B1z=20Berke=20G=C3=BCven=C3=A7?= Date: Wed, 8 Jul 2026 00:44:42 +0300 Subject: [PATCH 40/55] feat(postiz): update docker-compose and template for Temporal integration (#851) --- blueprints/postiz/docker-compose.yml | 80 ++++++++++++++++++++++++++-- blueprints/postiz/template.toml | 26 ++++++--- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/blueprints/postiz/docker-compose.yml b/blueprints/postiz/docker-compose.yml index dfbf3ae9..a00832dd 100644 --- a/blueprints/postiz/docker-compose.yml +++ b/blueprints/postiz/docker-compose.yml @@ -13,10 +13,25 @@ services: DATABASE_URL: "postgresql://${DB_USER}:${DB_PASSWORD}@postiz-postgres:5432/${DB_NAME}" REDIS_URL: "redis://postiz-redis:6379" BACKEND_INTERNAL_URL: "http://localhost:3000" + TEMPORAL_ADDRESS: "temporal:7233" IS_GENERAL: "true" + DISABLE_REGISTRATION: "false" + RUN_CRON: "true" + NOT_SECURED: "true" STORAGE_PROVIDER: "local" UPLOAD_DIRECTORY: "/uploads" NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads" + + # === Social Media API Settings === + # These variables should be defined in the Dokploy Environment tab. + # For more configuration options and details, please refer to the official documentation. + FACEBOOK_APP_ID: ${FACEBOOK_APP_ID} + FACEBOOK_APP_SECRET: ${FACEBOOK_APP_SECRET} + LINKEDIN_CLIENT_ID: ${LINKEDIN_CLIENT_ID} + LINKEDIN_CLIENT_SECRET: ${LINKEDIN_CLIENT_SECRET} + X_API_KEY: ${X_API_KEY} + X_API_SECRET: ${X_API_SECRET} + volumes: - postiz-config:/config/ - postiz-uploads:/uploads/ @@ -25,6 +40,8 @@ services: condition: service_healthy postiz-redis: condition: service_healthy + temporal: + condition: service_started postiz-postgres: image: postgres:17-alpine @@ -35,27 +52,80 @@ services: POSTGRES_USER: ${DB_USER} POSTGRES_DB: ${DB_NAME} volumes: - - postiz-postgres-data:/var/lib/postgresql/data + - postgres-volume:/var/lib/postgresql/data healthcheck: - test: pg_isready -U ${DB_USER} -d ${DB_NAME} + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] interval: 10s timeout: 3s retries: 3 + start_period: 10s postiz-redis: image: redis:7.2 restart: always healthcheck: - test: redis-cli ping + test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3 volumes: - postiz-redis-data:/data + temporal-elasticsearch: + image: elasticsearch:7.17.27 + environment: + - cluster.routing.allocation.disk.threshold_enabled=true + - cluster.routing.allocation.disk.watermark.low=512mb + - cluster.routing.allocation.disk.watermark.high=256mb + - cluster.routing.allocation.disk.watermark.flood_stage=128mb + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms256m -Xmx256m + - xpack.security.enabled=false + volumes: + - temporal-es-data:/usr/share/elasticsearch/data + + temporal-postgresql: + image: postgres:16 + environment: + POSTGRES_PASSWORD: ${TEMPORAL_PASSWORD} + POSTGRES_USER: ${TEMPORAL_USER} + volumes: + - temporal-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${TEMPORAL_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + temporal: + image: temporalio/auto-setup:1.28.1 + depends_on: + temporal-postgresql: + condition: service_healthy + temporal-elasticsearch: + condition: service_started + environment: + - DB=postgres12 + - DB_PORT=5432 + - POSTGRES_USER=${TEMPORAL_USER} + - POSTGRES_PWD=${TEMPORAL_PASSWORD} + - POSTGRES_SEEDS=temporal-postgresql + - ENABLE_ES=true + - ES_SEEDS=temporal-elasticsearch + - ES_VERSION=v7 + - TEMPORAL_NAMESPACE=default + healthcheck: + test: ["CMD", "tctl", "--address", "127.0.0.1:7233", "workflow", "list"] + interval: 15s + timeout: 10s + retries: 30 + start_period: 120s + volumes: - postiz-postgres-data: + postgres-volume: postiz-redis-data: postiz-config: - postiz-uploads: \ No newline at end of file + postiz-uploads: + temporal-es-data: + temporal-pg-data: \ No newline at end of file diff --git a/blueprints/postiz/template.toml b/blueprints/postiz/template.toml index 260b77af..4528e909 100644 --- a/blueprints/postiz/template.toml +++ b/blueprints/postiz/template.toml @@ -3,19 +3,29 @@ main_domain = "${domain}" db_password = "${password}" db_user = "postiz" db_name = "postiz" +temporal_password = "${password}" +temporal_user = "temporal" jwt_secret = "${base64:32}" [config] +env = [ + "POSTIZ_HOST=${main_domain}", + "JWT_SECRET=${jwt_secret}", + "DB_USER=${db_user}", + "DB_PASSWORD=${db_password}", + "DB_NAME=${db_name}", + "TEMPORAL_USER=${temporal_user}", + "TEMPORAL_PASSWORD=${temporal_password}", + "FACEBOOK_APP_ID=", + "FACEBOOK_APP_SECRET=", + "LINKEDIN_CLIENT_ID=", + "LINKEDIN_CLIENT_SECRET=", + "X_API_KEY=", + "X_API_SECRET=" +] mounts = [] [[config.domains]] serviceName = "postiz-app" -port = 5_000 +port = 5000 host = "${main_domain}" - -[config.env] -POSTIZ_HOST = "${main_domain}" -DB_PASSWORD = "${db_password}" -DB_USER = "${db_user}" -DB_NAME = "${db_name}" -JWT_SECRET = "${jwt_secret}" From 06904b17bea8ee3f48eafcfecaeb25a465f028c7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 7 Jul 2026 15:50:39 -0600 Subject: [PATCH 41/55] fix: repair CI broken by #875 (arche meta links + stray pnpm-workspace.yaml) (#977) * fix: repair CI broken by #875 merge - remove stray build-scripts/pnpm-workspace.yaml (placeholder content broke pnpm install in validate-docker-compose) - add missing links.website/docs to arche entry in meta.json (validate check) Co-Authored-By: Claude Fable 5 * chore: remove stray .vscode/settings.json introduced by #920 Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- .vscode/settings.json | 5 ----- build-scripts/pnpm-workspace.yaml | 2 -- meta.json | 4 +++- 3 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 build-scripts/pnpm-workspace.yaml diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 50e5eba2..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "chat.tools.terminal.autoApprove": { - "git config": true - } -} \ No newline at end of file diff --git a/build-scripts/pnpm-workspace.yaml b/build-scripts/pnpm-workspace.yaml deleted file mode 100644 index 00f6fc47..00000000 --- a/build-scripts/pnpm-workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -allowBuilds: - esbuild: set this to true or false diff --git a/meta.json b/meta.json index 6e2a3a34..00ceae04 100644 --- a/meta.json +++ b/meta.json @@ -456,7 +456,9 @@ "description": "Arche is a self-hosted AI workspace platform that spawns per-user OpenCode coding containers backed by a shared Knowledge Base. Requires the `arche-internal` external Docker network and `/opt/arche/{kb-content,kb-config,users}` directories on the host before deploy. Images are linux/amd64 by default; override `ARCHE_WEB_IMAGE` and `ARCHE_WORKSPACE_IMAGE` to the `-arm64` tag variants for ARM64 hosts.", "logo": "arche.svg", "links": { - "github": "https://github.com/peaberry-studio/arche" + "github": "https://github.com/peaberry-studio/arche", + "website": "https://github.com/peaberry-studio/arche", + "docs": "https://github.com/peaberry-studio/arche#readme" }, "tags": [ "ai", From 292716be4b1aed96fbb7874dffd86d68f999b3f9 Mon Sep 17 00:00:00 2001 From: Abdelatif Djeddou <155508467+BinkyTwin@users.noreply.github.com> Date: Tue, 7 Jul 2026 23:50:58 +0200 Subject: [PATCH 42/55] feat: add OpenSourcePOS template (#890) --- blueprints/opensourcepos/docker-compose.yml | 45 ++++++++++++++++++ blueprints/opensourcepos/opensourcepos.svg | 9 ++++ blueprints/opensourcepos/template.toml | 51 +++++++++++++++++++++ meta.json | 19 ++++++++ 4 files changed, 124 insertions(+) create mode 100644 blueprints/opensourcepos/docker-compose.yml create mode 100644 blueprints/opensourcepos/opensourcepos.svg create mode 100644 blueprints/opensourcepos/template.toml diff --git a/blueprints/opensourcepos/docker-compose.yml b/blueprints/opensourcepos/docker-compose.yml new file mode 100644 index 00000000..95d58960 --- /dev/null +++ b/blueprints/opensourcepos/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.8" + +services: + opensourcepos: + image: jekkos/opensourcepos:master + restart: unless-stopped + depends_on: + opensourcepos-db: + condition: service_healthy + expose: + - "80" + environment: + CI_ENVIRONMENT: production + ALLOWED_HOSTNAMES: ${ALLOWED_HOSTNAMES} + FORCE_HTTPS: ${FORCE_HTTPS} + PHP_TIMEZONE: ${PHP_TIMEZONE} + MYSQL_USERNAME: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + MYSQL_DB_NAME: ${MYSQL_DATABASE} + MYSQL_HOST_NAME: opensourcepos-db + volumes: + - opensourcepos-uploads:/app/public/uploads + - opensourcepos-logs:/app/writable/logs + + opensourcepos-db: + image: mariadb:10.11 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - opensourcepos-db:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + opensourcepos-db: + opensourcepos-uploads: + opensourcepos-logs: diff --git a/blueprints/opensourcepos/opensourcepos.svg b/blueprints/opensourcepos/opensourcepos.svg new file mode 100644 index 00000000..d63b2009 --- /dev/null +++ b/blueprints/opensourcepos/opensourcepos.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/blueprints/opensourcepos/template.toml b/blueprints/opensourcepos/template.toml new file mode 100644 index 00000000..d925ad0c --- /dev/null +++ b/blueprints/opensourcepos/template.toml @@ -0,0 +1,51 @@ +[variables] +main_domain = "${domain}" +mysql_root_password = "${password:32}" +mysql_password = "${password:32}" +php_timezone = "UTC" +force_https = "false" +allowed_hostnames = "${main_domain}" + +[[config.domains]] +serviceName = "opensourcepos" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# OpenSourcePOS + +This template deploys Open Source Point of Sale with MariaDB using the upstream Docker image and persistent storage for uploads, logs, and database data. + +## Access + +- URL: `http://${main_domain}` +- Default application user: `admin` +- Default application password: `pointofsale` + +Change the default application password immediately after the first login. + +## Hostnames and HTTPS + +OpenSourcePOS validates incoming host headers in production. The `allowed_hostnames` variable defaults to `${main_domain}` and can be set to a comma-separated list such as `pos.example.com,www.pos.example.com`. + +The template defaults `force_https` to `false` because Dokploy terminates TLS outside the container. Set it to `true` only when your deployment always reaches OpenSourcePOS through HTTPS and the reverse proxy sends the expected headers. + +## Storage + +The template creates persistent volumes for: + +- MariaDB data +- uploaded files +- application logs +""" + +[config.env] +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" +MYSQL_DATABASE = "ospos" +MYSQL_USER = "admin" +MYSQL_PASSWORD = "${mysql_password}" +PHP_TIMEZONE = "${php_timezone}" +FORCE_HTTPS = "${force_https}" +ALLOWED_HOSTNAMES = "${allowed_hostnames}" diff --git a/meta.json b/meta.json index 00ceae04..0fb24a51 100644 --- a/meta.json +++ b/meta.json @@ -5219,6 +5219,25 @@ "nginx" ] }, + { + "id": "opensourcepos", + "name": "OpenSourcePOS", + "version": "master", + "description": "OpenSourcePOS is a web-based point of sale system for managing sales, inventory, customers, and reports.", + "logo": "opensourcepos.svg", + "links": { + "github": "https://github.com/opensourcepos/opensourcepos", + "website": "https://opensourcepos.org/", + "docs": "https://github.com/opensourcepos/opensourcepos/blob/master/README.md" + }, + "tags": [ + "pos", + "retail", + "inventory", + "sales", + "business" + ] + }, { "id": "openspeedtest", "name": "OpenSpeedTest", From f2f4d85d4351095b2b6ce2e16fb40a119c13b675 Mon Sep 17 00:00:00 2001 From: Abdullah Istiak Date: Wed, 8 Jul 2026 03:51:01 +0600 Subject: [PATCH 43/55] Update Supabase Docker configuration and template (#872) * Update Supabase Docker configuration and template - Updated Supabase Studio and GoTrue images to the latest versions. - Modified healthcheck commands for the Studio service. - Enhanced environment variable configuration in docker-compose.yml for better flexibility. - Updated Logflare API key references and added new S3 protocol access keys in template.toml. - Introduced custom entrypoint script for Kong to handle Lua expressions and environment variable substitution. - Added new routes and plugins for enhanced authentication and authorization in Kong configuration. * Refactor Supabase Docker Compose and Template Configuration - Removed container name prefixes from services in docker-compose.yml for consistency. - Increased healthcheck retries and start periods for several services to improve stability. - Updated healthcheck timeout and retry configurations for the analytics service. - Adjusted appname generation logic in template.toml to ensure proper naming for services. - Cleaned up commented sections in template.toml for clarity. --- blueprints/supabase/docker-compose.yml | 306 +++++++++----------- blueprints/supabase/template.toml | 386 ++++++++++++++++++++----- 2 files changed, 440 insertions(+), 252 deletions(-) diff --git a/blueprints/supabase/docker-compose.yml b/blueprints/supabase/docker-compose.yml index 506ddc90..3e03fb2c 100644 --- a/blueprints/supabase/docker-compose.yml +++ b/blueprints/supabase/docker-compose.yml @@ -1,89 +1,107 @@ # Usage -# Start: docker compose up -# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up +# Start: docker compose up -d # Stop: docker compose down -# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans -# Reset everything: ./reset.sh +# Reset everything: ./reset.sh +# +# Dokploy: Kong is exposed on port 8000 for domain routing. Storage uses local file +# backend by default; edit the storage service env in this file to switch to S3/MinIO. name: supabase services: studio: - container_name: ${CONTAINER_PREFIX}-studio - image: supabase/studio:2025.04.21-sha-173cc56 + image: supabase/studio:2026.04.27-sha-5f60601 restart: unless-stopped healthcheck: test: [ - "CMD", - "node", - "-e", - "fetch('http://studio:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})" + "CMD-SHELL", + "node -e \"fetch('http://localhost:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})\"" ] timeout: 10s interval: 5s - retries: 3 + retries: 5 + start_period: 30s depends_on: analytics: condition: service_healthy environment: + HOSTNAME: "0.0.0.0" + STUDIO_PG_META_URL: http://meta:8080 + POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} + POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY} + PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} + PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} + DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION} DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT} OPENAI_API_KEY: ${OPENAI_API_KEY:-} - SUPABASE_URL: ${SUPABASE_PUBLIC_URL} + SUPABASE_URL: http://kong:8000 SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} AUTH_JWT_SECRET: ${JWT_SECRET} - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_API_KEY: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + LOGFLARE_URL: http://analytics:4000 - NEXT_PUBLIC_ENABLE_LOGS: true - # Comment to use Big Query backend for analytics + NEXT_PUBLIC_ENABLE_LOGS: "true" NEXT_ANALYTICS_BACKEND_PROVIDER: postgres - # Uncomment to use Big Query backend for analytics - # NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery + SNIPPETS_MANAGEMENT_FOLDER: /app/snippets + EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions + volumes: + - ../files/volumes/snippets:/app/snippets:Z + - ../files/volumes/functions:/app/edge-functions:Z kong: - container_name: ${CONTAINER_PREFIX}-kong - image: kong:2.8.1 + image: kong/kong:3.9.1 restart: unless-stopped - # ports: - # - ${KONG_HTTP_PORT}:8000/tcp - # - ${KONG_HTTPS_PORT}:8443/tcp + healthcheck: + test: ["CMD", "kong", "health"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + studio: + condition: service_healthy expose: - 8000 - 8443 volumes: - # https://github.com/supabase/supabase/issues/12661 - ../files/volumes/api/kong.yml:/home/kong/temp.yml:ro,z - depends_on: - analytics: - condition: service_healthy + - ../files/volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z environment: KONG_DATABASE: "off" - KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml - # https://github.com/supabase/cli/issues/14 + KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml KONG_DNS_ORDER: LAST,A,CNAME - KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth + KONG_DNS_NOT_FOUND_TTL: 1 + KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + KONG_PROXY_ACCESS_LOG: /dev/stdout combined SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-} + SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-} + ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-} + SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-} DASHBOARD_USERNAME: ${DASHBOARD_USERNAME} DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD} - CONTAINER_PREFIX: ${CONTAINER_PREFIX} - # https://unix.stackexchange.com/a/294837 - entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start' + entrypoint: ["/bin/bash", "/home/kong/kong-entrypoint.sh"] auth: - container_name: ${CONTAINER_PREFIX}-auth - image: supabase/gotrue:v2.171.0 + image: supabase/gotrue:v2.186.0 restart: unless-stopped healthcheck: test: @@ -100,12 +118,8 @@ services: retries: 3 depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: - # the next line seems required if you want to be able to send mails from the supabase GUI GOTRUE_MAILER_EXTERNAL_HOSTS: kong,${SUPABASE_HOST} GOTRUE_API_HOST: 0.0.0.0 GOTRUE_API_PORT: 9999 @@ -123,16 +137,12 @@ services: GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated GOTRUE_JWT_EXP: ${JWT_EXPIRY} GOTRUE_JWT_SECRET: ${JWT_SECRET} + GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]} GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM} - # Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile. - # GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true - - # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true - # GOTRUE_SMTP_MAX_FREQUENCY: 1s GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL} GOTRUE_SMTP_HOST: ${SMTP_HOST} GOTRUE_SMTP_PORT: ${SMTP_PORT} @@ -146,39 +156,18 @@ services: GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP} GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM} - # Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook - - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true" - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook" - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "" - - # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true" - # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt" - - # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true" - # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt" - - # GOTRUE_HOOK_SEND_SMS_ENABLED: "false" - # GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook" - # GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" - - # GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false" - # GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender" - # GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" rest: - container_name: ${CONTAINER_PREFIX}-rest - image: postgrest/postgrest:v12.2.11 + image: postgrest/postgrest:v14.8 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} + PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} PGRST_DB_ANON_ROLE: anon PGRST_JWT_SECRET: ${JWT_SECRET} PGRST_DB_USE_LEGACY_GUCS: "false" @@ -190,32 +179,21 @@ services: ] realtime: - # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain - container_name: realtime-dev.${CONTAINER_PREFIX}-realtime - image: supabase/realtime:v2.34.47 + image: supabase/realtime:v2.76.5 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy healthcheck: test: [ - "CMD", - "curl", - "-sSfL", - "--head", - "-o", - "/dev/null", - "-H", - "Authorization: Bearer ${ANON_KEY}", - "http://localhost:4000/api/tenants/realtime-dev/health" + "CMD-SHELL", + "curl -sSfL --head -o /dev/null -H \"Authorization: Bearer ${ANON_KEY}\" http://localhost:4000/api/tenants/realtime-dev/health" ] timeout: 5s - interval: 5s + interval: 30s retries: 3 + start_period: 10s environment: PORT: 4000 DB_HOST: ${POSTGRES_HOST} @@ -227,20 +205,25 @@ services: DB_ENC_KEY: supabaserealtime API_JWT_SECRET: ${JWT_SECRET} SECRET_KEY_BASE: ${SECRET_KEY_BASE} + METRICS_JWT_SECRET: ${JWT_SECRET} ERL_AFLAGS: -proto_dist inet_tcp DNS_NODES: "''" RLIMIT_NOFILE: "10000" APP_NAME: realtime - SEED_SELF_HOST: true - RUN_JANITOR: true + SEED_SELF_HOST: "true" + RUN_JANITOR: "true" + DISABLE_HEALTHCHECK_LOGGING: "true" - # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up storage: - container_name: ${CONTAINER_PREFIX}-storage - image: supabase/storage-api:v1.22.7 + image: supabase/storage-api:v1.48.26 restart: unless-stopped - volumes: - - ../files/volumes/storage:/var/lib/storage:z + depends_on: + db: + condition: service_healthy + rest: + condition: service_started + imgproxy: + condition: service_started healthcheck: test: [ @@ -254,33 +237,30 @@ services: timeout: 5s interval: 5s retries: 3 - depends_on: - db: - # Disable this if you are using an external Postgres database - condition: service_healthy - rest: - condition: service_started - imgproxy: - condition: service_started + start_period: 10s + volumes: + - ../files/volumes/storage:/var/lib/storage:z environment: ANON_KEY: ${ANON_KEY} SERVICE_KEY: ${SERVICE_ROLE_KEY} POSTGREST_URL: http://rest:3000 - PGRST_JWT_SECRET: ${JWT_SECRET} + AUTH_JWT_SECRET: ${JWT_SECRET} DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + STORAGE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} + REQUEST_ALLOW_X_FORWARDED_PATH: "true" FILE_SIZE_LIMIT: 52428800 STORAGE_BACKEND: file + GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET} FILE_STORAGE_BACKEND_PATH: /var/lib/storage - TENANT_ID: stub - # TODO: https://github.com/supabase/storage-api/issues/55 - REGION: stub - GLOBAL_S3_BUCKET: stub + TENANT_ID: ${STORAGE_TENANT_ID} + REGION: ${REGION} ENABLE_IMAGE_TRANSFORMATION: "true" IMGPROXY_URL: http://imgproxy:5001 + S3_PROTOCOL_ACCESS_KEY_ID: ${S3_PROTOCOL_ACCESS_KEY_ID} + S3_PROTOCOL_ACCESS_KEY_SECRET: ${S3_PROTOCOL_ACCESS_KEY_SECRET} imgproxy: - container_name: ${CONTAINER_PREFIX}-imgproxy - image: darthsim/imgproxy:v3.8.0 + image: darthsim/imgproxy:v3.30.1 restart: unless-stopped volumes: - ../files/volumes/storage:/var/lib/storage:z @@ -298,17 +278,14 @@ services: IMGPROXY_BIND: ":5001" IMGPROXY_LOCAL_FILESYSTEM_ROOT: / IMGPROXY_USE_ETAG: "true" - IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION} + IMGPROXY_AUTO_WEBP: ${IMGPROXY_AUTO_WEBP} + IMGPROXY_MAX_SRC_RESOLUTION: 16.8 meta: - container_name: ${CONTAINER_PREFIX}-meta - image: supabase/postgres-meta:v0.88.9 + image: supabase/postgres-meta:v0.96.3 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: PG_META_PORT: 8080 @@ -317,23 +294,26 @@ services: PG_META_DB_NAME: ${POSTGRES_DB} PG_META_DB_USER: supabase_admin PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + CRYPTO_KEY: ${PG_META_CRYPTO_KEY} functions: - container_name: ${CONTAINER_PREFIX}-edge-functions - image: supabase/edge-runtime:v1.67.4 + image: supabase/edge-runtime:v1.71.2 restart: unless-stopped volumes: - ../files/volumes/functions:/home/deno/functions:Z + - deno-cache:/root/.cache/deno depends_on: - analytics: + kong: condition: service_healthy environment: JWT_SECRET: ${JWT_SECRET} SUPABASE_URL: http://kong:8000 + SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_PUBLISHABLE_KEYS: "{\"default\":\"${SUPABASE_PUBLISHABLE_KEY:-}\"}" + SUPABASE_SECRET_KEYS: "{\"default\":\"${SUPABASE_SECRET_KEY:-}\"}" SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} - # TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786 VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}" command: [ @@ -343,32 +323,23 @@ services: ] analytics: - container_name: ${CONTAINER_PREFIX}-analytics - image: supabase/logflare:1.12.0 + image: supabase/logflare:1.36.1 restart: unless-stopped - # ports: - # - 4000:4000 expose: - 4000 - # Uncomment to use Big Query backend for analytics - # volumes: - # - type: bind - # source: ${PWD}/gcloud.json - # target: /opt/app/rel/logflare/bin/gcloud.json - # read_only: true + # First boot: Logflare seeds _analytics in Postgres; allow extra time before marking unhealthy. healthcheck: test: [ - "CMD", - "curl", - "http://localhost:4000/health" + "CMD-SHELL", + "curl -sSfL -o /dev/null http://localhost:4000/health" ] - timeout: 5s + timeout: 10s interval: 5s - retries: 10 + retries: 15 + start_period: 90s depends_on: db: - # Disable this if you are using an external Postgres database condition: service_healthy environment: LOGFLARE_NODE_HOST: 127.0.0.1 @@ -378,55 +349,42 @@ services: DB_PORT: ${POSTGRES_PORT} DB_PASSWORD: ${POSTGRES_PASSWORD} DB_SCHEMA: _analytics - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} - LOGFLARE_SINGLE_TENANT: true - LOGFLARE_SUPABASE_MODE: true + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + LOGFLARE_SINGLE_TENANT: "true" + LOGFLARE_SUPABASE_MODE: "true" LOGFLARE_MIN_CLUSTER_SIZE: 1 - - # Comment variables to use Big Query backend for analytics POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase POSTGRES_BACKEND_SCHEMA: _analytics LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true - # Uncomment to use Big Query backend for analytics - # GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID} - # GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER} - # Comment out everything below this point if you are using an external Postgres database db: - container_name: ${CONTAINER_PREFIX}-db - image: supabase/postgres:15.8.1.060 + image: supabase/postgres:15.8.1.085 restart: unless-stopped volumes: - ../files/volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z - # Must be superuser to create event trigger - ../files/volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z - # Must be superuser to alter reserved role - ../files/volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z - # Initialize the database settings with JWT_SECRET and JWT_EXP - ../files/volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z - # PGDATA directory is persisted between restarts - ../files/volumes/db/data:/var/lib/postgresql/data:Z - # Changes required for internal supabase data such as _analytics - ../files/volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z - # Changes required for Analytics support - ../files/volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z - # Changes required for Pooler support - ../files/volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z - # Use named volume to persist pgsodium decryption key between restarts - db-config:/etc/postgresql-custom healthcheck: test: [ - "CMD", - "pg_isready", - "-U", - "postgres", - "-h", - "localhost" + "CMD", + "pg_isready", + "-U", + "postgres", + "-h", + "localhost" ] interval: 5s timeout: 5s - retries: 10 + retries: 15 + start_period: 60s depends_on: vector: condition: service_healthy @@ -446,12 +404,11 @@ services: "-c", "config_file=/etc/postgresql/postgresql.conf", "-c", - "log_min_messages=fatal" # prevents Realtime polling queries from appearing in logs + "log_min_messages=fatal" ] vector: - container_name: ${CONTAINER_PREFIX}-vector - image: timberio/vector:0.28.1-alpine + image: timberio/vector:0.53.0-alpine restart: unless-stopped volumes: - ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z @@ -468,9 +425,10 @@ services: ] timeout: 5s interval: 5s - retries: 3 + retries: 5 + start_period: 10s environment: - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} command: [ "--config", @@ -479,14 +437,12 @@ services: security_opt: - "label=disable" - # Update the DATABASE_URL if you are using an external Postgres database supavisor: - container_name: ${CONTAINER_PREFIX}-pooler - image: supabase/supavisor:2.5.1 + image: supabase/supavisor:2.7.4 restart: unless-stopped - ports: # expose supavisor to the host to enable db pooler connection - - ${POSTGRES_PORT}:5432 - - ${POOLER_PROXY_PORT_TRANSACTION}:6543 + expose: + - "5432" + - "6543" volumes: - ../files/volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro,z healthcheck: @@ -502,19 +458,19 @@ services: ] interval: 10s timeout: 5s - retries: 5 + retries: 10 + start_period: 30s depends_on: db: condition: service_healthy - analytics: - condition: service_healthy environment: PORT: 4000 POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase - CLUSTER_POSTGRES: true + DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase + CLUSTER_POSTGRES: "true" SECRET_KEY_BASE: ${SECRET_KEY_BASE} VAULT_ENC_KEY: ${VAULT_ENC_KEY} API_JWT_SECRET: ${JWT_SECRET} @@ -525,6 +481,7 @@ services: POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE} POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN} POOLER_POOL_MODE: transaction + DB_POOL_SIZE: ${POOLER_DB_POOL_SIZE} command: [ "/bin/sh", @@ -533,4 +490,5 @@ services: ] volumes: - db-config: \ No newline at end of file + db-config: + deno-cache: diff --git a/blueprints/supabase/template.toml b/blueprints/supabase/template.toml index f70c93cc..0e76b7dd 100644 --- a/blueprints/supabase/template.toml +++ b/blueprints/supabase/template.toml @@ -2,11 +2,15 @@ main_domain = "${domain}" postgres_password = "${password:32}" dashboard_password = "${password:32}" -logflare_api_key = "${password:32}" +logflare_public_access_token = "${password:32}" +logflare_private_access_token = "${password:32}" +pg_meta_crypto_key = "${password:32}" +s3_protocol_access_key_id = "${password:24}" +s3_protocol_access_key_secret = "${password:48}" secret_key_base = "${password:64}" vault_enc_key = "${password:32}" jwt_secret = "${password:32}" -container_name_prefix = "${APP_NAME}-supabase" +pooler_tenant_id = "${uuid}" anon_key_payload = """{ "role": "anon", "iss": "supabase", @@ -34,12 +38,7 @@ env = [ '# - ADDITIONAL_REDIRECT_URLS, SITE_URL should point to application using supabase for authentication', '# They are used for redirecting after login/signup and gotrue will check them before sending emails', '# - POSTGRES_PORT, POOLER_PROXY_PORT_TRANSACTION should be changed if you are already running other instances of supabase', -'#', -'# Supabase uses container names in part of its configuration so it is important to keep them', -'# This template generates a random prefix for the container names to avoid conflicts', -'# If you change it you will need to update routes in the vector.yml file in advanced->mounts section', '############', -'CONTAINER_PREFIX=${container_name_prefix}', '', '############', '# Secrets', @@ -59,6 +58,9 @@ env = [ 'DASHBOARD_PASSWORD=${dashboard_password}', 'SECRET_KEY_BASE=${secret_key_base}', 'VAULT_ENC_KEY=${vault_enc_key}', +'PG_META_CRYPTO_KEY=${pg_meta_crypto_key}', +'LOGFLARE_PUBLIC_ACCESS_TOKEN=${logflare_public_access_token}', +'LOGFLARE_PRIVATE_ACCESS_TOKEN=${logflare_private_access_token}', '', '', '############', @@ -68,7 +70,7 @@ env = [ 'POSTGRES_HOST=db', 'POSTGRES_DB=postgres', 'POSTGRES_PORT=5432', -'# default user is postgres', +'POSTGRES_USER=postgres', '', '', '############', @@ -77,7 +79,8 @@ env = [ 'POOLER_PROXY_PORT_TRANSACTION=6543', 'POOLER_DEFAULT_POOL_SIZE=20', 'POOLER_MAX_CLIENT_CONN=100', -'POOLER_TENANT_ID=your-tenant-id', +'POOLER_TENANT_ID=${pooler_tenant_id}', +'POOLER_DB_POOL_SIZE=5', '', '', '############', @@ -94,6 +97,8 @@ env = [ '############', '', 'PGRST_DB_SCHEMAS=public,storage,graphql_public', +'PGRST_DB_MAX_ROWS=1000', +'PGRST_DB_EXTRA_SEARCH_PATH=public', '', '', '############', @@ -102,10 +107,11 @@ env = [ '', '## General', 'SITE_URL=http://localhost:3000', -'ADDITIONAL_REDIRECT_URLS=http://${main_domain}/*,http://localhost:3000/*', +'ADDITIONAL_REDIRECT_URLS=https://${main_domain}/*,http://localhost:3000/*', 'JWT_EXPIRY=3600', 'DISABLE_SIGNUP=false', -'API_EXTERNAL_URL=http://${main_domain}', +'API_EXTERNAL_URL=https://${main_domain}', +'JWT_KEYS=[]', '', '## Mailer Config', 'MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"', @@ -136,12 +142,19 @@ env = [ 'STUDIO_DEFAULT_ORGANIZATION=Default Organization', 'STUDIO_DEFAULT_PROJECT=Default Project', '', -'STUDIO_PORT=3000', -'# replace if you intend to use Studio outside of localhost', -'SUPABASE_PUBLIC_URL=http://${main_domain}', +'SUPABASE_PUBLIC_URL=https://${main_domain}', '', -'# Enable webp support', -'IMGPROXY_ENABLE_WEBP_DETECTION=true', +'############', +'# Storage - local file backend (default). Edit docker-compose.yml storage env for S3/MinIO.', +'############', +'', +'GLOBAL_S3_BUCKET=stub', +'REGION=stub', +'STORAGE_TENANT_ID=stub', +'S3_PROTOCOL_ACCESS_KEY_ID=${s3_protocol_access_key_id}', +'S3_PROTOCOL_ACCESS_KEY_SECRET=${s3_protocol_access_key_secret}', +'', +'IMGPROXY_AUTO_WEBP=true', '', '# Add your OpenAI API key to enable SQL Editor Assistant', 'OPENAI_API_KEY=', @@ -156,14 +169,8 @@ env = [ '', '############', '# Logs - Configuration for Logflare', -'# Please refer to https://supabase.com/docs/reference/self-hosting-analytics/introduction', '############', '', -'LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key', -'', -'# Change vector.toml sinks to reflect this change', -'LOGFLARE_API_KEY=${logflare_api_key}', -'', '# Docker socket location - this value will differ depending on your OS', 'DOCKER_SOCKET_LOCATION=/var/run/docker.sock', '', @@ -171,6 +178,40 @@ env = [ 'GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID', 'GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER'] +[[config.mounts]] +filePath = "/volumes/api/kong-entrypoint.sh" +content = """#!/bin/bash +# Custom entrypoint for Kong that builds Lua expressions for request-transformer +# and performs environment variable substitution in the declarative config. + +if [ -n "$SUPABASE_SECRET_KEY" ] && [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then + export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)" + export LUA_RT_WS_EXPR="\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)" +else + export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)" + export LUA_RT_WS_EXPR="\$(query_params.apikey)" +fi + +awk '{ + result = "" + rest = $0 + while (match(rest, /\$[A-Za-z_][A-Za-z_0-9]*/)) { + varname = substr(rest, RSTART + 1, RLENGTH - 1) + if (varname in ENVIRON) { + result = result substr(rest, 1, RSTART - 1) ENVIRON[varname] + } else { + result = result substr(rest, 1, RSTART + RLENGTH - 1) + } + rest = substr(rest, RSTART + RLENGTH) + } + print result rest +}' /home/kong/temp.yml > "$KONG_DECLARATIVE_CONFIG" + +sed -i '/^[[:space:]]*- key:[[:space:]]*$/d' "$KONG_DECLARATIVE_CONFIG" + +exec /entrypoint.sh kong docker-start +""" + [[config.mounts]] filePath = "/volumes/api/kong.yml" content = """_format_version: '2.1' @@ -184,9 +225,11 @@ consumers: - username: anon keyauth_credentials: - key: $SUPABASE_ANON_KEY + - key: $SUPABASE_PUBLISHABLE_KEY - username: service_role keyauth_credentials: - key: $SUPABASE_SERVICE_KEY + - key: $SUPABASE_SECRET_KEY ### ### Access Control List @@ -202,8 +245,8 @@ acls: ### basicauth_credentials: - consumer: DASHBOARD - username: $DASHBOARD_USERNAME - password: $DASHBOARD_PASSWORD + username: '$DASHBOARD_USERNAME' + password: '$DASHBOARD_PASSWORD' ### ### API Routes @@ -237,6 +280,33 @@ services: - /auth/v1/authorize plugins: - name: cors + - name: auth-v1-open-jwks + url: http://auth:9999/.well-known/jwks.json + routes: + - name: auth-v1-open-jwks + strip_path: true + paths: + - /auth/v1/.well-known/jwks.json + plugins: + - name: cors + - name: auth-v1-open-sso-acs + url: http://auth:9999/sso/saml/acs + routes: + - name: auth-v1-open-sso-acs + strip_path: true + paths: + - /sso/saml/acs + plugins: + - name: cors + - name: auth-v1-open-sso-metadata + url: http://auth:9999/sso/saml/metadata + routes: + - name: auth-v1-open-sso-metadata + strip_path: true + paths: + - /sso/saml/metadata + plugins: + - name: cors ## Secure Auth routes - name: auth-v1 @@ -252,6 +322,14 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -272,7 +350,15 @@ services: - name: cors - name: key-auth config: - hide_credentials: true + hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -298,7 +384,11 @@ services: config: add: headers: - - Content-Profile:graphql_public + - "Content-Profile: graphql_public" + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -309,7 +399,7 @@ services: ## Secure Realtime routes - name: realtime-v1-ws _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' - url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/socket + url: http://realtime:4000/socket protocol: ws routes: - name: realtime-v1-ws @@ -321,6 +411,14 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "x-api-key:$LUA_RT_WS_EXPR" + replace: + querystring: + - "apikey:$LUA_RT_WS_EXPR" - name: acl config: hide_groups_header: true @@ -328,8 +426,8 @@ services: - admin - anon - name: realtime-v1-rest - _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' - url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/api + _comment: 'Realtime: /realtime/v1/api/* -> http://realtime:4000/api/*' + url: http://realtime:4000/api protocol: http routes: - name: realtime-v1-rest @@ -341,13 +439,22 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true allow: - admin - anon - ## Storage routes: the storage server manages its own auth + + ## Storage routes - name: storage-v1 _comment: 'Storage: /storage/v1/* -> http://storage:5000/*' url: http://storage:5000/ @@ -358,11 +465,28 @@ services: - /storage/v1/ plugins: - name: cors + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" + - name: post-function + config: + access: + - | + local auth = kong.request.get_header("authorization") + if auth == nil or auth == "" or auth:find("^%s*$") then + kong.service.request.clear_header("authorization") + end ## Edge Functions routes - name: functions-v1 _comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*' url: http://functions:9000/ + read_timeout: 150000 routes: - name: functions-v1-all strip_path: true @@ -371,15 +495,16 @@ services: plugins: - name: cors - ## Analytics routes - - name: analytics-v1 - _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' - url: http://analytics:4000/ + ## OAuth 2.0 Authorization Server Metadata + - name: well-known-oauth + url: http://auth:9999/.well-known/oauth-authorization-server routes: - - name: analytics-v1-all + - name: well-known-oauth strip_path: true paths: - - /analytics/v1/ + - /.well-known/oauth-authorization-server + plugins: + - name: cors ## Secure Database routes - name: meta @@ -400,6 +525,32 @@ services: allow: - admin + - name: mcp-blocker + url: http://studio:3000/api/mcp + routes: + - name: mcp-blocker-route + strip_path: true + paths: + - /api/mcp + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + + - name: mcp + url: http://studio:3000/api/mcp + routes: + - name: mcp + strip_path: true + paths: + - /mcp + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + ## Protected Dashboard - catch all remaining routes - name: dashboard _comment: 'Studio: /* -> http://studio:3000/*' @@ -416,6 +567,10 @@ services: hide_credentials: true """ +[[config.mounts]] +filePath = "/volumes/snippets/.gitkeep" +content = "" + [[config.mounts]] filePath = "/volumes/db/init/data.sql" content = "" @@ -717,14 +872,25 @@ serve(async () => { [[config.mounts]] filePath = "/volumes/functions/main/index.ts" -content = """import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' +content = """import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' console.log('main function started') const JWT_SECRET = Deno.env.get('JWT_SECRET') +const SUPABASE_URL = Deno.env.get('SUPABASE_URL') const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' +let SUPABASE_JWT_KEYS: ReturnType | null = null +if (SUPABASE_URL) { + try { + SUPABASE_JWT_KEYS = jose.createRemoteJWKSet( + new URL('/auth/v1/.well-known/jwks.json', SUPABASE_URL) + ) + } catch (e) { + console.error('Failed to fetch JWKS from SUPABASE_URL:', e) + } +} + function getAuthToken(req: Request) { const authHeader = req.headers.get('authorization') if (!authHeader) { @@ -737,25 +903,53 @@ function getAuthToken(req: Request) { return token } -async function verifyJWT(jwt: string): Promise { +async function isValidLegacyJWT(jwt: string): Promise { + if (!JWT_SECRET) { + console.error('JWT_SECRET not available for HS256 token verification') + return false + } const encoder = new TextEncoder() const secretKey = encoder.encode(JWT_SECRET) try { await jose.jwtVerify(jwt, secretKey) - } catch (err) { - console.error(err) + } catch (e) { + console.error('Symmetric Legacy JWT verification error', e) return false } return true } -serve(async (req: Request) => { +async function isValidJWT(jwt: string): Promise { + if (!SUPABASE_JWT_KEYS) { + console.error('JWKS not available for ES256/RS256 token verification') + return false + } + try { + await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS) + } catch (e) { + console.error('Asymmetric JWT verification error', e) + return false + } + return true +} + +async function isValidHybridJWT(jwt: string): Promise { + const { alg: jwtAlgorithm } = jose.decodeProtectedHeader(jwt) + if (jwtAlgorithm === 'HS256') { + return await isValidLegacyJWT(jwt) + } + if (jwtAlgorithm === 'ES256' || jwtAlgorithm === 'RS256') { + return await isValidJWT(jwt) + } + return false +} + +Deno.serve(async (req: Request) => { if (req.method !== 'OPTIONS' && VERIFY_JWT) { try { const token = getAuthToken(req) - const isValidJWT = await verifyJWT(token) - - if (!isValidJWT) { + const isValid = await isValidHybridJWT(token) + if (!isValid) { return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { status: 401, headers: { 'Content-Type': 'application/json' }, @@ -822,8 +1016,6 @@ content = """api: sources: docker_host: type: docker_logs - exclude_containers: - - ${container_name_prefix}-vector transforms: project_logs: @@ -833,7 +1025,15 @@ transforms: source: |- .project = "default" .event_message = del(.message) - .appname = replace!(del(.container_name), "${container_name_prefix}", "supabase") + compose_service, label_err = get(.label, ["com.docker.compose.service"]) + if label_err != null || compose_service == null { + abort + } + compose_service = to_string!(compose_service) + if compose_service == "vector" { + abort + } + .appname = "supabase-" + compose_service del(.container_created_at) del(.container_id) del(.source_type) @@ -847,12 +1047,12 @@ transforms: inputs: - project_logs route: - kong: '.appname == "supabase-kong"' + kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"' auth: '.appname == "supabase-auth"' rest: '.appname == "supabase-rest"' - realtime: '.appname == "realtime-dev.${CONTAINER_PREFIX}-realtime"' + realtime: '.appname == "supabase-realtime"' storage: '.appname == "supabase-storage"' - functions: '.appname == "supabase-edge-functions"' + functions: '.appname == "supabase-functions"' db: '.appname == "supabase-db"' # Ignores non nginx errors since they are related with kong booting up kong_logs: @@ -866,15 +1066,17 @@ transforms: .metadata.request.headers.referer = req.referer .metadata.request.headers.user_agent = req.agent .metadata.request.headers.cf_connecting_ip = req.client - .metadata.request.method = req.method - .metadata.request.path = req.path - .metadata.request.protocol = req.protocol .metadata.response.status_code = req.status + url, split_err = split(req.request, " ") + if split_err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } } if err != null { abort } - # Ignores non nginx errors since they are related with kong booting up kong_err: type: remap inputs: @@ -918,14 +1120,18 @@ transforms: parsed, err = parse_regex(.event_message, r'^(?P