diff --git a/.github/workflows/audit-all-blueprints.yml b/.github/workflows/audit-all-blueprints.yml new file mode 100644 index 000000000..f6a37c50d --- /dev/null +++ b/.github/workflows/audit-all-blueprints.yml @@ -0,0 +1,163 @@ +name: Audit All Blueprints + +# The per-PR validators only look at blueprints a PR touches, which is the right +# call for CI cost but means a template is checked when it lands and then never +# again. When a rule is added or tightened, every existing blueprint that breaks +# it stays broken with nothing surfacing it (see #1047). +# +# This job runs the same validators over ALL blueprints on a schedule, so drift +# is reported instead of accumulating. It never runs on pull_request, so it +# cannot block a contributor for a pre-existing problem they did not introduce. + +on: + schedule: + # Mondays, 04:00 UTC + - cron: "0 4 * * 1" + workflow_dispatch: + inputs: + fail_on_findings: + description: "Exit non-zero when findings exist (default: report only)" + type: boolean + default: false + +permissions: + contents: read + +jobs: + audit: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.16.0 + + - name: Set up pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: Install dependencies + run: cd build-scripts && pnpm install + + - name: Validate per-template metadata + run: node build-scripts/generate-meta.js --check + + - name: Audit every blueprint + id: audit + run: | + # $RUNNER_TEMP is per-job; also cleared explicitly so a re-run or a + # partially-executed earlier step can never leak into the summary. + WORK="${RUNNER_TEMP:-/tmp}" + OUT="$WORK/validator-out.txt" + REPORT="$WORK/audit-report.md" + SUMMARY="$WORK/audit-summary.md" + rm -f "$OUT" "$REPORT" "$SUMMARY" + + COMPOSE_FAILED="" + TOML_FAILED="" + + for dir in blueprints/*/; do + id="$(basename "$dir")" + + # Run unconditionally: a blueprint with no docker-compose.yml or no + # template.toml is itself a finding, and the validators already + # report a missing file clearly. Skipping them would hide exactly + # the blueprints most likely to be broken. + if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts \ + --file "../$dir/docker-compose.yml") > "$OUT" 2>&1; then + COMPOSE_FAILED="$COMPOSE_FAILED $id" + { + echo "### \`$id\` — docker-compose.yml" + echo '```' + grep '❌' "$OUT" || cat "$OUT" + echo '```' + } >> "$REPORT" + fi + + if ! (cd build-scripts && pnpm exec tsx validate-template.ts \ + --dir "../$dir") > "$OUT" 2>&1; then + TOML_FAILED="$TOML_FAILED $id" + { + echo "### \`$id\` — template.toml" + echo '```' + grep '❌' "$OUT" || cat "$OUT" + echo '```' + } >> "$REPORT" + fi + done + + COMPOSE_COUNT=$(echo $COMPOSE_FAILED | wc -w | tr -d ' ') + TOML_COUNT=$(echo $TOML_FAILED | wc -w | tr -d ' ') + TOTAL=$(ls -d blueprints/*/ | wc -l | tr -d ' ') + + { + echo "## Blueprint audit" + echo "" + echo "| check | failing | of |" + echo "| --- | --- | --- |" + echo "| \`validate-docker-compose.ts\` | $COMPOSE_COUNT | $TOTAL |" + echo "| \`validate-template.ts\` | $TOML_COUNT | $TOTAL |" + echo "" + } > "$SUMMARY" + + if [ -f "$REPORT" ]; then + cat "$REPORT" >> "$SUMMARY" + else + echo "No findings." >> "$SUMMARY" + fi + + # The step summary caps at ~1MB and truncates silently past it, which + # would hide findings exactly as the backlog grows. Bound what we + # append and point at the artifact for the rest. + SUMMARY_LIMIT=$((512 * 1024)) + SUMMARY_BYTES=$(wc -c < "$SUMMARY" | tr -d ' ') + + if [ "$SUMMARY_BYTES" -gt "$SUMMARY_LIMIT" ]; then + # Emit the header table only, never a byte-sliced prefix: cutting + # mid-``` leaves an unclosed code fence, which swallows the notice + # below it and makes the truncation invisible in the rendered summary. + { + head -n 7 "$SUMMARY" + echo "" + echo "_Report omitted: $SUMMARY_BYTES bytes exceeds the $SUMMARY_LIMIT byte budget for a job summary._" + echo "_Download the \`blueprint-audit\` artifact for the complete output._" + } >> "$GITHUB_STEP_SUMMARY" + else + cat "$SUMMARY" >> "$GITHUB_STEP_SUMMARY" + fi + + # Always published, so the full report survives regardless of size. + cp "$SUMMARY" "$WORK/blueprint-audit.md" + echo "report_path=$WORK/blueprint-audit.md" >> "$GITHUB_OUTPUT" + + echo "compose_failed=$COMPOSE_COUNT" >> "$GITHUB_OUTPUT" + echo "toml_failed=$TOML_COUNT" >> "$GITHUB_OUTPUT" + + if [ "$COMPOSE_COUNT" != "0" ] || [ "$TOML_COUNT" != "0" ]; then + echo "::warning::$COMPOSE_COUNT compose and $TOML_COUNT template.toml findings across $TOTAL blueprints" + fi + + - name: Upload the full report + if: always() && steps.audit.outputs.report_path != '' + uses: actions/upload-artifact@v4 + with: + name: blueprint-audit + path: ${{ steps.audit.outputs.report_path }} + retention-days: 30 + + - name: Fail if requested + # `inputs` is only populated for workflow_dispatch; on a schedule run it + # is not available, so gate on the event first and read the value through + # github.event.inputs, which is an empty string when absent. + if: >- + github.event_name == 'workflow_dispatch' && + github.event.inputs.fail_on_findings == 'true' && + (steps.audit.outputs.compose_failed != '0' || steps.audit.outputs.toml_failed != '0') + run: | + echo "❌ Findings present and fail_on_findings was requested." + exit 1 diff --git a/README.md b/README.md index bba2bda3e..1ae549179 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ services: - 3000:3000 # Instead use this way: - ports: + expose: - 3000 ``` diff --git a/blueprints/9router/9router.png b/blueprints/9router/9router.png new file mode 100644 index 000000000..af467dea5 Binary files /dev/null and b/blueprints/9router/9router.png differ diff --git a/blueprints/9router/docker-compose.yml b/blueprints/9router/docker-compose.yml new file mode 100644 index 000000000..163952263 --- /dev/null +++ b/blueprints/9router/docker-compose.yml @@ -0,0 +1,32 @@ +version: "3.8" + +services: + 9router: + image: decolua/9router:0.5.40 + restart: unless-stopped + environment: + DATA_DIR: /app/data + PORT: "20128" + HOSTNAME: "0.0.0.0" + NODE_ENV: production + JWT_SECRET: ${JWT_SECRET} + INITIAL_PASSWORD: ${INITIAL_PASSWORD} + API_KEY_SECRET: ${API_KEY_SECRET} + MACHINE_ID_SALT: ${MACHINE_ID_SALT} + BASE_URL: http://${ROUTER_HOST} + NEXT_PUBLIC_BASE_URL: http://${ROUTER_HOST} + HEADROOM_URL: http://headroom:8787 + AUTH_COOKIE_SECURE: "false" + volumes: + - 9router-data:/app/data + depends_on: + - headroom + + # Optional token-reduction proxy that 9Router calls through HEADROOM_URL. + # Shipped as a sidecar because the 9Router image does not bundle it. + headroom: + image: ghcr.io/chopratejas/headroom:0.6.7 + restart: unless-stopped + +volumes: + 9router-data: diff --git a/blueprints/9router/meta.json b/blueprints/9router/meta.json new file mode 100644 index 000000000..9339f3e33 --- /dev/null +++ b/blueprints/9router/meta.json @@ -0,0 +1,17 @@ +{ + "id": "9router", + "name": "9Router", + "version": "0.5.40", + "description": "Self-hosted AI model router that rotates requests across 40+ providers with automatic fallback, exposing an OpenAI-compatible endpoint for coding agents.", + "logo": "9router.png", + "links": { + "github": "https://github.com/decolua/9router", + "website": "https://9router.com", + "docs": "https://github.com/decolua/9router/blob/master/DOCKER.md" + }, + "tags": [ + "ai", + "proxy", + "developer-tools" + ] +} diff --git a/blueprints/9router/template.toml b/blueprints/9router/template.toml new file mode 100644 index 000000000..b01ec0916 --- /dev/null +++ b/blueprints/9router/template.toml @@ -0,0 +1,21 @@ +[variables] +main_domain = "${domain}" +jwt_secret = "${password:64}" +initial_password = "${password:32}" +api_key_secret = "${password:64}" +machine_id_salt = "${password:32}" + +[config] +env = [ + "ROUTER_HOST=${main_domain}", + "JWT_SECRET=${jwt_secret}", + "INITIAL_PASSWORD=${initial_password}", + "API_KEY_SECRET=${api_key_secret}", + "MACHINE_ID_SALT=${machine_id_salt}", +] +mounts = [] + +[[config.domains]] +serviceName = "9router" +port = 20128 +host = "${main_domain}" diff --git a/blueprints/ackee/docker-compose.yml b/blueprints/ackee/docker-compose.yml index 43e4220f1..90255c69a 100644 --- a/blueprints/ackee/docker-compose.yml +++ b/blueprints/ackee/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: ackee: image: electerious/ackee:3.4.2 - ports: + expose: - "3000" environment: - ACKEE_USERNAME=${ACKEE_USERNAME} diff --git a/blueprints/activepieces/docker-compose.yml b/blueprints/activepieces/docker-compose.yml index a5511e7fa..da06d316f 100644 --- a/blueprints/activepieces/docker-compose.yml +++ b/blueprints/activepieces/docker-compose.yml @@ -61,4 +61,4 @@ services: volumes: postgres_data: - redis_data: \ No newline at end of file + redis_data: diff --git a/blueprints/adguardhome/docker-compose.yml b/blueprints/adguardhome/docker-compose.yml index 72b5be339..bfda4d317 100644 --- a/blueprints/adguardhome/docker-compose.yml +++ b/blueprints/adguardhome/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — DNS (53/tcp+udp), DHCP (67/68) and DNS-over-TLS/QUIC (853) are not HTTP protocols; clients must reach them directly on the host, Traefik cannot route them. version: "3.8" services: adguardhome: @@ -10,11 +11,12 @@ services: - "68:68/tcp" # DHCP Client - "853:853/tcp" # DNS over TLS, DNS-over-QUIC - "853:853/udp" # DNS over TLS, DNS-over-QUIC - - "6060:6060/tcp" # HTTP (pprof) + expose: + - 6060 # HTTP (pprof) — debug endpoint, no need to publish it on the host volumes: - adguardhome-work:/opt/adguardhome/work - adguardhome-conf:/opt/adguardhome/conf volumes: adguardhome-work: {} - adguardhome-conf: {} \ No newline at end of file + adguardhome-conf: {} diff --git a/blueprints/adminer/docker-compose.yml b/blueprints/adminer/docker-compose.yml index 29d7c270c..3674d2946 100644 --- a/blueprints/adminer/docker-compose.yml +++ b/blueprints/adminer/docker-compose.yml @@ -3,5 +3,5 @@ services: adminer: image: adminer:4.8.1 restart: unless-stopped - ports: - - 8080 \ No newline at end of file + expose: + - 8080 diff --git a/blueprints/affinepro/docker-compose.yml b/blueprints/affinepro/docker-compose.yml index e8a905d5d..0af8e2cfa 100644 --- a/blueprints/affinepro/docker-compose.yml +++ b/blueprints/affinepro/docker-compose.yml @@ -3,7 +3,7 @@ services: affinepro: image: ghcr.io/toeverything/affine:stable restart: unless-stopped - ports: + expose: - 3010 volumes: - affine-storage:/root/.affine/storage diff --git a/blueprints/agent-zero/docker-compose.yml b/blueprints/agent-zero/docker-compose.yml index fceed3cf5..187d506e6 100644 --- a/blueprints/agent-zero/docker-compose.yml +++ b/blueprints/agent-zero/docker-compose.yml @@ -11,4 +11,4 @@ services: volumes: - agent-zero-data:/a0/usr volumes: - agent-zero-data: \ No newline at end of file + agent-zero-data: diff --git a/blueprints/agentdvr/docker-compose.yml b/blueprints/agentdvr/docker-compose.yml index 8dc4e7c37..a56e702cf 100644 --- a/blueprints/agentdvr/docker-compose.yml +++ b/blueprints/agentdvr/docker-compose.yml @@ -11,12 +11,11 @@ services: - agentdvr-config:/AgentDVR/Media/XML/ - agentdvr-media:/AgentDVR/Media/WebServerRoot/Media/ - agentdvr-commands:/AgentDVR/Commands/ - ports: + expose: - 8090 - 3478/udp - 50000-50100/udp - volumes: agentdvr-config: {} agentdvr-media: {} - agentdvr-commands: {} \ No newline at end of file + agentdvr-commands: {} diff --git a/blueprints/agentdvr/template.toml b/blueprints/agentdvr/template.toml index fd4096077..9216aafff 100644 --- a/blueprints/agentdvr/template.toml +++ b/blueprints/agentdvr/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" timezone = "America/New_York" [config] +mounts = [] [[config.domains]] serviceName = "agentdvr" port = 8090 @@ -10,5 +11,3 @@ host = "${main_domain}" [config.env] TIMEZONE = "${timezone}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/akaunting/template.toml b/blueprints/akaunting/template.toml index b78c412d4..a3c1680e8 100644 --- a/blueprints/akaunting/template.toml +++ b/blueprints/akaunting/template.toml @@ -24,6 +24,7 @@ port = 80 host = "${main_domain}" [config] +mounts = [] env = [ # App "APP_URL=${main_domain}", @@ -50,5 +51,4 @@ env = [ "MYSQL_RANDOM_ROOT_PASSWORD=yes" ] -# No custom mounts needed; volumes are already defined in compose. -[[config.mounts]] +# No custom mounts needed; volumes are already defined in compose. \ No newline at end of file diff --git a/blueprints/alist/docker-compose.yml b/blueprints/alist/docker-compose.yml index 1577387f3..6311c7403 100644 --- a/blueprints/alist/docker-compose.yml +++ b/blueprints/alist/docker-compose.yml @@ -11,4 +11,4 @@ services: restart: unless-stopped volumes: - alist-data: \ No newline at end of file + alist-data: diff --git a/blueprints/alltube/docker-compose.yml b/blueprints/alltube/docker-compose.yml index c04e5238f..90bbc5045 100644 --- a/blueprints/alltube/docker-compose.yml +++ b/blueprints/alltube/docker-compose.yml @@ -3,10 +3,10 @@ services: alltube: image: dnomd343/alltube:latest restart: unless-stopped - ports: + expose: - 80 environment: - TITLE=${TITLE} - CONVERT=${CONVERT} - STREAM=${STREAM} - - REMUX=${REMUX} \ No newline at end of file + - REMUX=${REMUX} diff --git a/blueprints/ampache/docker-compose.yml b/blueprints/ampache/docker-compose.yml index 0af8f418a..f4a1da455 100644 --- a/blueprints/ampache/docker-compose.yml +++ b/blueprints/ampache/docker-compose.yml @@ -3,7 +3,7 @@ services: ampache: image: ampache/ampache:latest restart: unless-stopped - ports: + expose: - 80 volumes: - config:/var/www/config @@ -14,4 +14,4 @@ services: volumes: config: {} log: {} - mysql: {} \ No newline at end of file + mysql: {} diff --git a/blueprints/anonupload/docker-compose.yml b/blueprints/anonupload/docker-compose.yml index 9d88e7987..7ff3b2ea0 100644 --- a/blueprints/anonupload/docker-compose.yml +++ b/blueprints/anonupload/docker-compose.yml @@ -3,7 +3,7 @@ services: anonupload: image: ghcr.io/supernova3339/anonfiles:1 restart: unless-stopped - ports: + expose: - 80 environment: - ADMIN_EMAIL=${ADMIN_EMAIL} @@ -13,4 +13,4 @@ services: - uploads:/var/www/html/uploads volumes: - uploads: {} \ No newline at end of file + uploads: {} diff --git a/blueprints/answer/docker-compose.yml b/blueprints/answer/docker-compose.yml index 2b9fc3440..b23f87022 100644 --- a/blueprints/answer/docker-compose.yml +++ b/blueprints/answer/docker-compose.yml @@ -1,7 +1,7 @@ services: answer: image: apache/answer:1.4.1 - ports: + expose: - '80' restart: on-failure volumes: diff --git a/blueprints/anubis/docker-compose.yml b/blueprints/anubis/docker-compose.yml index 51b30b38f..70b8b7d5d 100644 --- a/blueprints/anubis/docker-compose.yml +++ b/blueprints/anubis/docker-compose.yml @@ -3,7 +3,7 @@ services: anubis: image: ghcr.io/techarohq/anubis:latest restart: unless-stopped - ports: + expose: - "8923" # Anubis default port environment: # Required: Point to your frontend service locally diff --git a/blueprints/anythingllm/docker-compose.yml b/blueprints/anythingllm/docker-compose.yml index 248753a74..e7ac94273 100644 --- a/blueprints/anythingllm/docker-compose.yml +++ b/blueprints/anythingllm/docker-compose.yml @@ -3,7 +3,7 @@ services: anythingllm: image: mintplexlabs/anythingllm:latest restart: unless-stopped - ports: + expose: - 3001 environment: - STORAGE_DIR=/app/server/storage @@ -13,4 +13,4 @@ services: - SYS_ADMIN volumes: - storage: {} \ No newline at end of file + storage: {} diff --git a/blueprints/anytype/docker-compose.yml b/blueprints/anytype/docker-compose.yml index 5f3f70cee..36fc86329 100644 --- a/blueprints/anytype/docker-compose.yml +++ b/blueprints/anytype/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — Anytype clients speak the any-sync protocol (TCP 33010, UDP/QUIC 33020) directly against the server; it is not HTTP, so Traefik cannot route it. # Example: Any-Sync-Bundle with embedded MongoDB and Redis (all-in-one image) # # Usage: diff --git a/blueprints/apprise-api/docker-compose.yml b/blueprints/apprise-api/docker-compose.yml index cc46a5e35..af502e520 100644 --- a/blueprints/apprise-api/docker-compose.yml +++ b/blueprints/apprise-api/docker-compose.yml @@ -3,7 +3,7 @@ services: apprise-api: image: linuxserver/apprise-api:latest restart: unless-stopped - ports: + expose: - 8000 environment: - PUID=1000 @@ -13,4 +13,4 @@ services: - config:/config volumes: - config: {} \ No newline at end of file + config: {} diff --git a/blueprints/appsmith/docker-compose.yml b/blueprints/appsmith/docker-compose.yml index e22be5579..2f8914e99 100644 --- a/blueprints/appsmith/docker-compose.yml +++ b/blueprints/appsmith/docker-compose.yml @@ -7,4 +7,4 @@ services: restart: unless-stopped volumes: - appsmith-data: \ No newline at end of file + appsmith-data: diff --git a/blueprints/arangodb/docker-compose.yml b/blueprints/arangodb/docker-compose.yml index 65301d164..9137789fb 100644 --- a/blueprints/arangodb/docker-compose.yml +++ b/blueprints/arangodb/docker-compose.yml @@ -3,7 +3,7 @@ services: arangodb: image: arangodb:3.12.4 restart: unless-stopped - ports: + expose: - 8529 environment: - ARANGO_ROOT_PASSWORD=${ARANGO_PASSWORD} @@ -11,4 +11,4 @@ services: - data:/var/lib/arangodb3 volumes: - data: {} \ No newline at end of file + data: {} diff --git a/blueprints/archivebox/docker-compose.yml b/blueprints/archivebox/docker-compose.yml index 2f1493451..17868f237 100644 --- a/blueprints/archivebox/docker-compose.yml +++ b/blueprints/archivebox/docker-compose.yml @@ -9,8 +9,7 @@ services: PUBLIC_SNAPSHOTS: ${PUBLIC_SNAPSHOTS} volumes: - archivebox_data:/data - ports: + expose: - "8000" - volumes: archivebox_data: diff --git a/blueprints/argilla/docker-compose.yml b/blueprints/argilla/docker-compose.yml index 735350dc3..1e2976eae 100644 --- a/blueprints/argilla/docker-compose.yml +++ b/blueprints/argilla/docker-compose.yml @@ -3,7 +3,7 @@ services: argilla-web: image: argilla/argilla-server:latest restart: unless-stopped - ports: + expose: - 6900 environment: - ARGILLA_HOME_PATH=/var/lib/argilla @@ -74,4 +74,4 @@ volumes: argilladata: {} elasticdata: {} dbdata: {} - redisdata: {} \ No newline at end of file + redisdata: {} diff --git a/blueprints/audiobookshelf/docker-compose.yml b/blueprints/audiobookshelf/docker-compose.yml index 821da7619..262cfa7e5 100644 --- a/blueprints/audiobookshelf/docker-compose.yml +++ b/blueprints/audiobookshelf/docker-compose.yml @@ -3,7 +3,7 @@ services: audiobookshelf: image: ghcr.io/advplyr/audiobookshelf:2.19.4 restart: unless-stopped - ports: + expose: - 80 environment: - TZ=UTC @@ -14,4 +14,4 @@ services: volumes: config: {} - metadata: {} \ No newline at end of file + metadata: {} diff --git a/blueprints/authelia/docker-compose.yml b/blueprints/authelia/docker-compose.yml index fa65571bf..6cd5c77d1 100644 --- a/blueprints/authelia/docker-compose.yml +++ b/blueprints/authelia/docker-compose.yml @@ -16,9 +16,8 @@ services: condition: service_healthy postgres: condition: service_healthy - ports: + expose: - 9091 - redis: image: redis:7-alpine restart: unless-stopped @@ -52,4 +51,4 @@ services: volumes: authelia_config: redis_data: - postgres_data: \ No newline at end of file + postgres_data: diff --git a/blueprints/authorizer/docker-compose.yml b/blueprints/authorizer/docker-compose.yml index c99e92e84..05f034d4b 100644 --- a/blueprints/authorizer/docker-compose.yml +++ b/blueprints/authorizer/docker-compose.yml @@ -3,7 +3,7 @@ services: authorizer: image: lakhansamani/authorizer:1.4.4 restart: unless-stopped - ports: + expose: - 8080 environment: - DATABASE_TYPE=postgres @@ -37,4 +37,4 @@ services: volumes: db_data: {} - redis_data: {} \ No newline at end of file + redis_data: {} diff --git a/blueprints/autobase/docker-compose.yml b/blueprints/autobase/docker-compose.yml index 7582c9c63..7b931d9e7 100644 --- a/blueprints/autobase/docker-compose.yml +++ b/blueprints/autobase/docker-compose.yml @@ -2,7 +2,7 @@ services: autobase-console: image: autobase/console:2.7.2 restart: unless-stopped - ports: + expose: - "80" - "8080" environment: diff --git a/blueprints/azuracast/docker-compose.yml b/blueprints/azuracast/docker-compose.yml index 536802000..c5b61391f 100644 --- a/blueprints/azuracast/docker-compose.yml +++ b/blueprints/azuracast/docker-compose.yml @@ -2,7 +2,7 @@ services: azuracast: image: ghcr.io/azuracast/azuracast:latest restart: unless-stopped - ports: + expose: - 80 volumes: - azuracast-station-data:/var/azuracast/stations @@ -36,4 +36,4 @@ volumes: azuracast-data: {} azuracast-uploads: {} azuracast-backups: {} - mariadb-data: {} \ No newline at end of file + mariadb-data: {} diff --git a/blueprints/azuracast/template.toml b/blueprints/azuracast/template.toml index cee2f3932..1c970bc4a 100644 --- a/blueprints/azuracast/template.toml +++ b/blueprints/azuracast/template.toml @@ -4,6 +4,7 @@ mysql_root_password = "${password:32}" mysql_password = "${password:16}" [config] +mounts = [] [[config.domains]] serviceName = "azuracast" port = 80 @@ -27,5 +28,3 @@ NGINX_RADIO_PORTS = "8000,8010,8020,8030,8040,8050" PREFER_RELEASE_BUILDS = "true" COMPOSER_PLUGIN_MODE = "false" ADDITIONAL_MEDIA_SYNC_WORKER_COUNT = "0" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/babybuddy/docker-compose.yml b/blueprints/babybuddy/docker-compose.yml index c07d90621..8642ef789 100644 --- a/blueprints/babybuddy/docker-compose.yml +++ b/blueprints/babybuddy/docker-compose.yml @@ -3,7 +3,7 @@ services: babybuddy: image: linuxserver/babybuddy:2.7.0 restart: unless-stopped - ports: + expose: - 8000 environment: - PUID=1000 @@ -16,4 +16,4 @@ services: - config:/config volumes: - config: {} \ No newline at end of file + config: {} diff --git a/blueprints/backrest/docker-compose.yml b/blueprints/backrest/docker-compose.yml index cdff2dfba..9fc714721 100644 --- a/blueprints/backrest/docker-compose.yml +++ b/blueprints/backrest/docker-compose.yml @@ -2,7 +2,7 @@ services: backrest: image: garethgeorge/backrest:v1.7.3 restart: unless-stopped - ports: + expose: - 9898 environment: - BACKREST_PORT=9898 diff --git a/blueprints/baikal/docker-compose.yml b/blueprints/baikal/docker-compose.yml index d0ba2a6a8..80169c6a6 100644 --- a/blueprints/baikal/docker-compose.yml +++ b/blueprints/baikal/docker-compose.yml @@ -3,7 +3,7 @@ services: baikal: image: ckulka/baikal:nginx-php8.2 restart: unless-stopped - ports: + expose: - 80 environment: - TZ=UTC @@ -13,4 +13,4 @@ services: volumes: config: {} - data: {} \ No newline at end of file + data: {} diff --git a/blueprints/barrage/docker-compose.yml b/blueprints/barrage/docker-compose.yml index 554f967fb..79c048f22 100644 --- a/blueprints/barrage/docker-compose.yml +++ b/blueprints/barrage/docker-compose.yml @@ -3,11 +3,11 @@ services: barrage: image: maulik9898/barrage:0.3.0 restart: unless-stopped - ports: + expose: - 3000 environment: - NEXTAUTH_SECRET=${NEXTAUTH_SECRET} - NEXTAUTH_URL=http://${DOMAIN} - DELUGE_URL=${DELUGE_URL} - DELUGE_PASSWORD=${DELUGE_PASSWORD} - - BARRAGE_PASSWORD=${BARRAGE_PASSWORD} \ No newline at end of file + - BARRAGE_PASSWORD=${BARRAGE_PASSWORD} diff --git a/blueprints/bazarr/docker-compose.yml b/blueprints/bazarr/docker-compose.yml index a0df5be7e..557ba5eb5 100644 --- a/blueprints/bazarr/docker-compose.yml +++ b/blueprints/bazarr/docker-compose.yml @@ -3,7 +3,7 @@ services: bazarr: image: lscr.io/linuxserver/bazarr:1.5.1 restart: unless-stopped - ports: + expose: - 6767 environment: - PUID=1000 @@ -15,4 +15,4 @@ services: - ${TV_PATH}:/tv volumes: - config: {} \ No newline at end of file + config: {} diff --git a/blueprints/beszel/docker-compose.yml b/blueprints/beszel/docker-compose.yml index a14372ae0..c5d9957f1 100644 --- a/blueprints/beszel/docker-compose.yml +++ b/blueprints/beszel/docker-compose.yml @@ -3,11 +3,11 @@ services: beszel: image: henrygd/beszel:0.10.2 restart: unless-stopped - ports: + expose: - 8090 volumes: - beszel_data:/beszel_data - /var/run/docker.sock:/var/run/docker.sock:ro volumes: - beszel_data: {} \ No newline at end of file + beszel_data: {} diff --git a/blueprints/bigcapital/docker-compose.yml b/blueprints/bigcapital/docker-compose.yml index aa8ff4876..605e87122 100644 --- a/blueprints/bigcapital/docker-compose.yml +++ b/blueprints/bigcapital/docker-compose.yml @@ -4,10 +4,8 @@ services: restart: unless-stopped depends_on: - server - ports: + expose: - '80' - - server: image: bigcapitalhq/server:latest restart: unless-stopped @@ -18,9 +16,8 @@ services: condition: service_started redis: condition: service_started - ports: + expose: - '3000' - environment: # Mail - MAIL_HOST=${MAIL_HOST} @@ -97,9 +94,8 @@ services: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} volumes: - mysql:/var/lib/mysql - ports: + expose: - '3306' - healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$$MYSQL_ROOT_PASSWORD || exit 1"] interval: 10s @@ -110,7 +106,7 @@ services: mongo: image: mongo:7 restart: unless-stopped - ports: + expose: - '27017' volumes: - mongo:/data/db @@ -119,7 +115,7 @@ services: redis: image: redis:7-alpine restart: unless-stopped - ports: + expose: - '6379' volumes: - redis:/data @@ -133,10 +129,8 @@ services: gotenberg: image: gotenberg/gotenberg:7 restart: unless-stopped - ports: + expose: - '9000' - - volumes: mysql: name: bigcapital_mysql diff --git a/blueprints/bigcapital/template.toml b/blueprints/bigcapital/template.toml index 750237629..5052e075b 100644 --- a/blueprints/bigcapital/template.toml +++ b/blueprints/bigcapital/template.toml @@ -72,7 +72,7 @@ s3_endpoint = "" s3_bucket = "" [config] - +mounts = [] [[config.domains]] serviceName = "webapp" port = 80 @@ -165,6 +165,3 @@ MYSQL_DATABASE = "${system_db_name}" MYSQL_USER = "${db_user}" MYSQL_PASSWORD = "${db_password}" MYSQL_ROOT_PASSWORD = "${db_root_password}" - -[[config.mounts]] - diff --git a/blueprints/blender/docker-compose.yml b/blueprints/blender/docker-compose.yml index 893f3deea..8d7c6484a 100644 --- a/blueprints/blender/docker-compose.yml +++ b/blueprints/blender/docker-compose.yml @@ -19,7 +19,7 @@ services: - PGID=1000 - TZ=Etc/UTC - SUBFOLDER=/ #optional - ports: + expose: - 3000 - 3001 restart: unless-stopped diff --git a/blueprints/blinko/docker-compose.yml b/blueprints/blinko/docker-compose.yml index 0d110956e..43736a5ee 100644 --- a/blueprints/blinko/docker-compose.yml +++ b/blueprints/blinko/docker-compose.yml @@ -16,9 +16,8 @@ services: options: max-size: "10m" max-file: "3" - ports: + expose: - 1111 - blinko-postgres: image: postgres:14 restart: always diff --git a/blueprints/bluesky-pds/docker-compose.yml b/blueprints/bluesky-pds/docker-compose.yml index 4f9ae9a2b..7048c3703 100644 --- a/blueprints/bluesky-pds/docker-compose.yml +++ b/blueprints/bluesky-pds/docker-compose.yml @@ -45,4 +45,4 @@ services: timeout: 10s retries: 10 volumes: - pds-data: \ No newline at end of file + pds-data: diff --git a/blueprints/booklore/docker-compose.yml b/blueprints/booklore/docker-compose.yml index b092368c6..cf79e01e2 100644 --- a/blueprints/booklore/docker-compose.yml +++ b/blueprints/booklore/docker-compose.yml @@ -8,7 +8,7 @@ services: depends_on: mariadb: condition: service_healthy - ports: + expose: - 6060 volumes: - booklore-data:/app/data diff --git a/blueprints/booklore/template.toml b/blueprints/booklore/template.toml index 32347e8f6..944ee27ef 100644 --- a/blueprints/booklore/template.toml +++ b/blueprints/booklore/template.toml @@ -4,6 +4,7 @@ app_password = "${password:32}" db_root_password = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "booklore" port = 6060 @@ -20,5 +21,3 @@ MYSQL_USER = "booklore" MYSQL_PASSWORD = "${app_password}" # API Key MYSQL_ROOT_PASSWORD = "${db_root_password}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/bookstack/docker-compose.yml b/blueprints/bookstack/docker-compose.yml index e8fd4001e..acfff69e8 100644 --- a/blueprints/bookstack/docker-compose.yml +++ b/blueprints/bookstack/docker-compose.yml @@ -3,7 +3,7 @@ services: bookstack: image: lscr.io/linuxserver/bookstack:24.12.1 restart: unless-stopped - ports: + expose: - 80 environment: - PUID=1000 @@ -32,4 +32,4 @@ services: volumes: config: {} - db_data: {} \ No newline at end of file + db_data: {} diff --git a/blueprints/borgitory/template.toml b/blueprints/borgitory/template.toml index 985e423ea..cdde9507c 100644 --- a/blueprints/borgitory/template.toml +++ b/blueprints/borgitory/template.toml @@ -2,24 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "borgitory" port = 8000 host = "${main_domain}" [config.env] - -[[config.mounts]] -name = "borgitory-data" -mountPath = "/app/data" -description = "Database and encryption key storage" - -[[config.mounts]] -name = "borgitory-sources" -mountPath = "/mnt/sources" -description = "Sources to back up (read-only)" - -[[config.mounts]] -name = "borgitory-repos" -mountPath = "/mnt/repos" -description = "Borg repositories (read-only)" \ No newline at end of file diff --git a/blueprints/botpress/docker-compose.yml b/blueprints/botpress/docker-compose.yml index 7afb52b2e..64c0209a9 100644 --- a/blueprints/botpress/docker-compose.yml +++ b/blueprints/botpress/docker-compose.yml @@ -3,7 +3,7 @@ services: botpress: image: botpress/server:12.31.9 restart: unless-stopped - ports: + expose: - 81 environment: - BP_HOST=0.0.0.0 @@ -31,4 +31,4 @@ services: volumes: data: {} - db_data: {} \ No newline at end of file + db_data: {} diff --git a/blueprints/budget-board/docker-compose.yml b/blueprints/budget-board/docker-compose.yml index 80ec95118..86cc73de9 100644 --- a/blueprints/budget-board/docker-compose.yml +++ b/blueprints/budget-board/docker-compose.yml @@ -22,7 +22,7 @@ services: environment: VITE_API_URL: http://budget-board-server PORT: 6253 - ports: + expose: - 6253 depends_on: - budget-board-server diff --git a/blueprints/budget-board/template.toml b/blueprints/budget-board/template.toml index 6f09e75db..04be3320f 100644 --- a/blueprints/budget-board/template.toml +++ b/blueprints/budget-board/template.toml @@ -3,18 +3,12 @@ main_domain = "${domain}" postgres_password = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "budget-board-client" port = 6253 host = "${main_domain}" path = "/" - [config.env] POSTGRES_PASSWORD = "${postgres_password}" - - -[[config.mounts]] -source = "../files/db-data" -target = "/var/lib/postgresql/data" -type = "bind" \ No newline at end of file diff --git a/blueprints/bytebase/docker-compose.yml b/blueprints/bytebase/docker-compose.yml index 0b4bba92d..8cef0a385 100644 --- a/blueprints/bytebase/docker-compose.yml +++ b/blueprints/bytebase/docker-compose.yml @@ -3,7 +3,7 @@ services: bytebase: image: bytebase/bytebase:3.3.0 restart: unless-stopped - ports: + expose: - 8080 environment: - PG_URL=postgres://postgres:${DB_PASSWORD}@bytebase-db:5432/bytebase @@ -24,4 +24,4 @@ services: volumes: data: {} - db_data: {} \ No newline at end of file + db_data: {} diff --git a/blueprints/bytestash/docker-compose.yml b/blueprints/bytestash/docker-compose.yml index 3fa7e037f..677190a65 100644 --- a/blueprints/bytestash/docker-compose.yml +++ b/blueprints/bytestash/docker-compose.yml @@ -4,7 +4,7 @@ services: bytestash: image: ghcr.io/jordan-dalby/bytestash:1.5.6 restart: unless-stopped - ports: + expose: - "5000" environment: - BASE_PATH= @@ -24,4 +24,4 @@ services: - snippets:/data/snippets volumes: - snippets: \ No newline at end of file + snippets: diff --git a/blueprints/calibre/docker-compose.yml b/blueprints/calibre/docker-compose.yml index 3594e7bc0..07a43cb5f 100644 --- a/blueprints/calibre/docker-compose.yml +++ b/blueprints/calibre/docker-compose.yml @@ -9,7 +9,7 @@ services: - TZ=Etc/UTC - PUID=1000 - PGID=1000 - ports: + expose: - 8080 volumes: - books:/books @@ -17,4 +17,4 @@ services: volumes: books: - data: \ No newline at end of file + data: diff --git a/blueprints/carbone/docker-compose.yml b/blueprints/carbone/docker-compose.yml index e908c103d..8f771ca5d 100644 --- a/blueprints/carbone/docker-compose.yml +++ b/blueprints/carbone/docker-compose.yml @@ -7,10 +7,10 @@ services: environment: - CARBONE_EE_LICENSE=${CARBONE_KEY} - CARBONE_EE_STUDIO=true - ports: + expose: - 4000 volumes: - template:/app/template volumes: - template: \ No newline at end of file + template: diff --git a/blueprints/casdoor/docker-compose.yml b/blueprints/casdoor/docker-compose.yml index 6cc1c07e3..65f663302 100644 --- a/blueprints/casdoor/docker-compose.yml +++ b/blueprints/casdoor/docker-compose.yml @@ -30,4 +30,4 @@ services: volumes: casdoor-postgres-data: - casdoor-data: \ No newline at end of file + casdoor-data: diff --git a/blueprints/certmate/docker-compose.yml b/blueprints/certmate/docker-compose.yml index d7dde9dcb..6e9031f82 100644 --- a/blueprints/certmate/docker-compose.yml +++ b/blueprints/certmate/docker-compose.yml @@ -11,7 +11,7 @@ services: - CLOUDFLARE_TOKEN=${CLOUDFLARE_TOKEN} - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL} - LOG_LEVEL=${LOG_LEVEL} - ports: + expose: - 8000 volumes: - certmate_certificates:/app/certificates diff --git a/blueprints/changedetection/docker-compose.yml b/blueprints/changedetection/docker-compose.yml index 11dc9d07e..9258e29e5 100644 --- a/blueprints/changedetection/docker-compose.yml +++ b/blueprints/changedetection/docker-compose.yml @@ -4,10 +4,10 @@ services: changedetection: image: ghcr.io/dgtlmoon/changedetection.io:0.49 restart: unless-stopped - ports: + expose: - 5000 volumes: - datastore:/datastore volumes: - datastore: \ No newline at end of file + datastore: diff --git a/blueprints/chatto/docker-compose.yml b/blueprints/chatto/docker-compose.yml index 3edb99d3b..b9013af99 100644 --- a/blueprints/chatto/docker-compose.yml +++ b/blueprints/chatto/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: chatto: - image: ghcr.io/chattocorp/chatto:0.3.8 + image: ghcr.io/chattocorp/chatto:0.4 restart: unless-stopped expose: - "4000" diff --git a/blueprints/chatto/meta.json b/blueprints/chatto/meta.json index d420b2e39..b48f120a8 100644 --- a/blueprints/chatto/meta.json +++ b/blueprints/chatto/meta.json @@ -1,7 +1,7 @@ { "id": "chatto", "name": "Chatto (single binary)", - "version": "0.3.8", + "version": "0.4.x", "description": "A fully-featured real-time chat application for teams and communities. This template deploys the single Chatto binary with embedded NATS. Note: email/password registration requires SMTP to be configured after deployment; voice/video calls are not included in the single-binary setup.", "logo": "logo.png", "links": { diff --git a/blueprints/chatwoot/docker-compose.yml b/blueprints/chatwoot/docker-compose.yml index a5604573b..17f25a77f 100644 --- a/blueprints/chatwoot/docker-compose.yml +++ b/blueprints/chatwoot/docker-compose.yml @@ -76,4 +76,4 @@ services: volumes: chatwoot-storage: chatwoot-postgres-data: - chatwoot-redis-data: \ No newline at end of file + chatwoot-redis-data: diff --git a/blueprints/checkcle/template.toml b/blueprints/checkcle/template.toml index 887698a22..718fa2582 100644 --- a/blueprints/checkcle/template.toml +++ b/blueprints/checkcle/template.toml @@ -2,13 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "checkcle" port = 8090 host = "${main_domain}" [config.env] - -[[config.mounts]] -source = "checkcle-data" -target = "/mnt/pb_data" \ No newline at end of file diff --git a/blueprints/checkmate/docker-compose.yml b/blueprints/checkmate/docker-compose.yml index bfb69bbc3..8855e24da 100644 --- a/blueprints/checkmate/docker-compose.yml +++ b/blueprints/checkmate/docker-compose.yml @@ -3,7 +3,7 @@ services: server: image: ghcr.io/bluewave-labs/checkmate-backend-mono:latest restart: always - ports: + expose: - 52345 environment: - UPTIME_APP_API_BASE_URL=${UPTIME_APP_API_BASE_URL} diff --git a/blueprints/chevereto/docker-compose.yml b/blueprints/chevereto/docker-compose.yml index 89c05e058..5e08351b1 100644 --- a/blueprints/chevereto/docker-compose.yml +++ b/blueprints/chevereto/docker-compose.yml @@ -15,7 +15,7 @@ services: - CHEVERETO_HTTPS=0 - CHEVERETO_MAX_POST_SIZE=2G - CHEVERETO_MAX_UPLOAD_SIZE=2G - ports: + expose: - 80 volumes: - storage:/var/www/html/images/ @@ -35,4 +35,4 @@ services: volumes: storage: - mysql: \ No newline at end of file + mysql: diff --git a/blueprints/chibisafe/docker-compose.yml b/blueprints/chibisafe/docker-compose.yml index 573cb16a7..db4462567 100644 --- a/blueprints/chibisafe/docker-compose.yml +++ b/blueprints/chibisafe/docker-compose.yml @@ -6,8 +6,6 @@ services: chibisafe: image: chibisafe/chibisafe:latest - networks: - - chibinet environment: - BASE_API_URL=http://chibisafe_server:8000 restart: unless-stopped @@ -20,8 +18,6 @@ services: chibisafe_server: image: chibisafe/chibisafe-server:latest - networks: - - chibinet volumes: - database:/app/database:rw - uploads:/app/uploads:rw @@ -43,8 +39,6 @@ services: caddy: image: caddy:2-alpine - networks: - - chibinet volumes: - ../files/Caddyfile:/etc/caddy/Caddyfile:ro - uploads:/app/uploads:ro @@ -62,8 +56,3 @@ volumes: database: uploads: logs: - -networks: - chibinet: - driver: bridge - internal: true diff --git a/blueprints/chiefonboarding/docker-compose.yml b/blueprints/chiefonboarding/docker-compose.yml index cf57cb1ad..a2621e430 100644 --- a/blueprints/chiefonboarding/docker-compose.yml +++ b/blueprints/chiefonboarding/docker-compose.yml @@ -8,7 +8,7 @@ services: - SECRET_KEY=${SECRET_KEY} - DATABASE_URL=postgres://postgres:${DB_PASSWORD}@db:5432/chiefonboarding - ALLOWED_HOSTS=${DOMAIN} - ports: + expose: - 8000 depends_on: - db @@ -23,4 +23,4 @@ services: - postgres_data:/var/lib/postgresql/data volumes: - postgres_data: \ No newline at end of file + postgres_data: diff --git a/blueprints/chirpstack/docker-compose.yml b/blueprints/chirpstack/docker-compose.yml index 85b32a12a..62894dfd1 100644 --- a/blueprints/chirpstack/docker-compose.yml +++ b/blueprints/chirpstack/docker-compose.yml @@ -5,7 +5,7 @@ services: restart: unless-stopped volumes: - ../files/chirpstack:/etc/chirpstack - ports: + expose: - 8080 environment: - MQTT_BROKER_HOST=mosquitto @@ -21,7 +21,7 @@ services: restart: unless-stopped volumes: - ../files/chirpstack-gateway-bridge:/etc/chirpstack-gateway-bridge - ports: + expose: - 1700/udp environment: - INTEGRATION__MQTT__EVENT_TOPIC_TEMPLATE=eu868/gateway/{{ .GatewayID }}/event/{{ .EventType }} @@ -36,7 +36,7 @@ services: restart: unless-stopped volumes: - ../files/chirpstack-gateway-bridge:/etc/chirpstack-gateway-bridge - ports: + expose: - 3001 depends_on: - mosquitto @@ -45,7 +45,7 @@ services: image: chirpstack/chirpstack-rest-api:4 restart: unless-stopped command: --server chirpstack:8080 --bind 0.0.0.0:8090 --insecure - ports: + expose: - 8090 depends_on: - chirpstack @@ -73,9 +73,8 @@ services: restart: unless-stopped volumes: - ../files/mosquitto/config/:/mosquitto/config/ - ports: + expose: - 1883 - volumes: postgresqldata: redisdata: diff --git a/blueprints/chromium/docker-compose.yml b/blueprints/chromium/docker-compose.yml index f6f766e2a..f555f7f2f 100644 --- a/blueprints/chromium/docker-compose.yml +++ b/blueprints/chromium/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: chromium: image: lscr.io/linuxserver/chromium:5f5dd27e-ls102 - ports: + expose: - "3000" environment: - PUID=1000 @@ -13,4 +13,4 @@ services: - config:/config volumes: - config: \ No newline at end of file + config: diff --git a/blueprints/classicpress/docker-compose.yml b/blueprints/classicpress/docker-compose.yml index 1f15f43bc..3842985d2 100644 --- a/blueprints/classicpress/docker-compose.yml +++ b/blueprints/classicpress/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: classicpress: image: classicpress/classicpress:php8.3-apache - ports: + expose: - "80" environment: - WORDPRESS_DB_HOST=db @@ -27,4 +27,4 @@ services: volumes: wordpress-data: - db-data: \ No newline at end of file + db-data: diff --git a/blueprints/cloud9/docker-compose.yml b/blueprints/cloud9/docker-compose.yml index cf836bbff..6a27f7489 100644 --- a/blueprints/cloud9/docker-compose.yml +++ b/blueprints/cloud9/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: cloud9: image: lscr.io/linuxserver/cloud9:1.29.2 - ports: + expose: - "8000" environment: - PUID=1000 @@ -17,4 +17,4 @@ services: - code:/code volumes: - code: \ No newline at end of file + code: diff --git a/blueprints/cloudcommander/docker-compose.yml b/blueprints/cloudcommander/docker-compose.yml index 33690f09f..5a29f3a54 100644 --- a/blueprints/cloudcommander/docker-compose.yml +++ b/blueprints/cloudcommander/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: cloudcmd: image: coderaiser/cloudcmd:18.5.1 - ports: + expose: - "80" environment: - CLOUDCMD_ROOT=/mnt/fs @@ -12,4 +12,4 @@ services: - CLOUDCMD_PASSWORD=${PASSWORD} volumes: - /root:/root - - /:/mnt/fs \ No newline at end of file + - /:/mnt/fs diff --git a/blueprints/cockpit/docker-compose.yml b/blueprints/cockpit/docker-compose.yml index fbc99a8e8..7c6a652ae 100644 --- a/blueprints/cockpit/docker-compose.yml +++ b/blueprints/cockpit/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: cockpit: image: cockpithq/cockpit:core-2.11.0 - ports: + expose: - "80" environment: - COCKPIT_SESSION_NAME=cockpit @@ -27,4 +27,4 @@ services: volumes: config: storage: - mongo-data: \ No newline at end of file + mongo-data: diff --git a/blueprints/codex-docs/docker-compose.yml b/blueprints/codex-docs/docker-compose.yml index 99fb55975..78549d072 100644 --- a/blueprints/codex-docs/docker-compose.yml +++ b/blueprints/codex-docs/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: codex: image: ghcr.io/codex-team/codex.docs:v2.2 - ports: + expose: - "3000" environment: - APP_CONFIG_database_driver=mongodb @@ -28,4 +28,4 @@ services: volumes: uploads: db: - mongo-data: \ No newline at end of file + mongo-data: diff --git a/blueprints/collabora-office/docker-compose.yml b/blueprints/collabora-office/docker-compose.yml index 2d6faeb62..8d8bd91b3 100644 --- a/blueprints/collabora-office/docker-compose.yml +++ b/blueprints/collabora-office/docker-compose.yml @@ -3,10 +3,10 @@ version: "3" services: collabora: image: collabora/code:latest - ports: + expose: - "9980" environment: - domain=${DOMAIN} - username=${USERNAME} - password=${PASSWORD} - - extra_params=--o:ssl.enable=false \ No newline at end of file + - extra_params=--o:ssl.enable=false diff --git a/blueprints/commafeed/docker-compose.yml b/blueprints/commafeed/docker-compose.yml index 3996e54c6..6037c626a 100644 --- a/blueprints/commafeed/docker-compose.yml +++ b/blueprints/commafeed/docker-compose.yml @@ -5,5 +5,5 @@ services: restart: unless-stopped volumes: - ../files/commafeed-data:/commafeed/data - ports: + expose: - 8082 diff --git a/blueprints/commento/docker-compose.yml b/blueprints/commento/docker-compose.yml index ebac8389e..971e1d496 100644 --- a/blueprints/commento/docker-compose.yml +++ b/blueprints/commento/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: commento: image: registry.gitlab.com/commento/commento:v1.8.0 - ports: + expose: - "8080" environment: - COMMENTO_ORIGIN=${COMMENTO_ORIGIN} @@ -19,4 +19,4 @@ services: - postgres-data:/var/lib/postgresql/data volumes: - postgres-data: \ No newline at end of file + postgres-data: diff --git a/blueprints/commentoplusplus/docker-compose.yml b/blueprints/commentoplusplus/docker-compose.yml index fc555f3ae..98af00e6d 100644 --- a/blueprints/commentoplusplus/docker-compose.yml +++ b/blueprints/commentoplusplus/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: commentoplusplus: image: caroga/commentoplusplus:v1.8.7 - ports: + expose: - "8080" environment: - COMMENTO_ORIGIN=${COMMENTO_ORIGIN} @@ -22,4 +22,4 @@ services: - postgres-data:/var/lib/postgresql/data volumes: - postgres-data: \ No newline at end of file + postgres-data: diff --git a/blueprints/conduwuit/conduwuit.svg b/blueprints/conduwuit/conduwuit.svg deleted file mode 100644 index 162a3d9e3..000000000 --- a/blueprints/conduwuit/conduwuit.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - diff --git a/blueprints/conduwuit/docker-compose.yml b/blueprints/conduwuit/docker-compose.yml deleted file mode 100644 index 7945d6c97..000000000 --- a/blueprints/conduwuit/docker-compose.yml +++ /dev/null @@ -1,48 +0,0 @@ -# conduwuit -# https://conduwuit.puppyirl.gay/deploying/docker-compose.yml - -services: - homeserver: - image: girlbossceo/conduwuit:latest - restart: unless-stopped - ports: - - 8448:6167 - volumes: - - db:/var/lib/conduwuit - #- ./conduwuit.toml:/etc/conduwuit.toml - environment: - # Edit this in your Dokploy Environment - CONDUWUIT_SERVER_NAME: ${CONDUWUIT_SERVER_NAME} - - CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit - CONDUWUIT_PORT: 6167 - CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB - - CONDUWUIT_ALLOW_REGISTRATION: 'true' - CONDUWUIT_REGISTRATION_TOKEN: ${CONDUWUIT_REGISTRATION_TOKEN} - - CONDUWUIT_ALLOW_FEDERATION: 'true' - CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' - CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]' - #CONDUWUIT_LOG: warn,state_res=warn - CONDUWUIT_ADDRESS: 0.0.0.0 - - # Uncomment if you mapped config toml in volumes - #CONDUWUIT_CONFIG: '/etc/conduwuit.toml' - - ### Uncomment if you want to use your own Element-Web App. - ### Note: You need to provide a config.json for Element and you also need a second - ### Domain or Subdomain for the communication between Element and conduwuit - ### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md - # element-web: - # image: vectorim/element-web:latest - # restart: unless-stopped - # ports: - # - 8009:80 - # volumes: - # - ./element_config.json:/app/config.json - # depends_on: - # - homeserver - -volumes: - db: diff --git a/blueprints/conduwuit/meta.json b/blueprints/conduwuit/meta.json deleted file mode 100644 index 0e346276d..000000000 --- a/blueprints/conduwuit/meta.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "conduwuit", - "name": "Conduwuit", - "version": "latest", - "description": "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", - "logo": "conduwuit.svg", - "links": { - "github": "https://github.com/girlbossceo/conduwuit", - "website": "https://conduwuit.puppyirl.gay", - "docs": "https://conduwuit.puppyirl.gay/configuration.html" - }, - "tags": [ - "backend", - "chat", - "communication", - "matrix", - "server" - ] -} diff --git a/blueprints/conduwuit/template.toml b/blueprints/conduwuit/template.toml deleted file mode 100644 index f7bc73016..000000000 --- a/blueprints/conduwuit/template.toml +++ /dev/null @@ -1,15 +0,0 @@ -[variables] -main_domain = "${domain}" -registration_token = "${password:20}" - -[config] -env = [ - "CONDUWUIT_SERVER_NAME=${main_domain}", - "CONDUWUIT_REGISTRATION_TOKEN=${registration_token}", -] -mounts = [] - -[[config.domains]] -serviceName = "homeserver" -port = 6_167 -host = "${main_domain}" diff --git a/blueprints/confluence/docker-compose.yml b/blueprints/confluence/docker-compose.yml index 3142cfe79..53a069377 100644 --- a/blueprints/confluence/docker-compose.yml +++ b/blueprints/confluence/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: confluence: image: atlassian/confluence-server:8.6-ubuntu-jdk17 - ports: + expose: - "8090" volumes: - confluence-data:/var/atlassian/application-data/confluence @@ -15,4 +15,4 @@ services: - CATALINA_CONNECTOR_SECURE=true volumes: - confluence-data: \ No newline at end of file + confluence-data: diff --git a/blueprints/convertx/docker-compose.yml b/blueprints/convertx/docker-compose.yml index c372ce505..d5de4b0c9 100644 --- a/blueprints/convertx/docker-compose.yml +++ b/blueprints/convertx/docker-compose.yml @@ -3,7 +3,7 @@ services: convertx: image: ghcr.io/c4illin/convertx restart: unless-stopped - ports: + expose: - 3000 environment: - JWT_SECRET=${JWT_SECRET} diff --git a/blueprints/convertx/template.toml b/blueprints/convertx/template.toml index 0851cc293..a90d75e3f 100644 --- a/blueprints/convertx/template.toml +++ b/blueprints/convertx/template.toml @@ -11,6 +11,7 @@ hide_history = "false" language = "en" [config] +mounts = [] [[config.domains]] serviceName = "convertx" port = 3000 @@ -26,8 +27,3 @@ WEBROOT = "${webroot}" FFMPEG_ARGS = "${ffmpeg_args}" HIDE_HISTORY = "${hide_history}" LANGUAGE = "${language}" - -[[config.mounts]] -source = "../files/data" -target = "/app/data" -type = "bind" \ No newline at end of file diff --git a/blueprints/cookie-cloud/template.toml b/blueprints/cookie-cloud/template.toml index 748d39bb7..9cd830a82 100644 --- a/blueprints/cookie-cloud/template.toml +++ b/blueprints/cookie-cloud/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "cookiecloud" port = 8088 @@ -9,6 +10,3 @@ host = "${main_domain}" [config.env] # No environment variables required by default - -[[config.mounts]] -# Data volume mount is defined in docker-compose.yml \ No newline at end of file diff --git a/blueprints/coralproject/docker-compose.yml b/blueprints/coralproject/docker-compose.yml index f308098d5..55483c8fa 100644 --- a/blueprints/coralproject/docker-compose.yml +++ b/blueprints/coralproject/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: coral: image: coralproject/talk:9.7.0 - ports: + expose: - "3000" environment: - MONGODB_URI=${MONGODB_URI} @@ -30,4 +30,4 @@ services: volumes: mongo-data: - redis-data: \ No newline at end of file + redis-data: diff --git a/blueprints/couchdb/docker-compose.yml b/blueprints/couchdb/docker-compose.yml index cb00bf69d..2263c7421 100644 --- a/blueprints/couchdb/docker-compose.yml +++ b/blueprints/couchdb/docker-compose.yml @@ -3,7 +3,7 @@ version: '3.8' services: couchdb: image: couchdb:latest - ports: + expose: - '5984' volumes: - couchdb-data:/opt/couchdb/data diff --git a/blueprints/creed/creed.svg b/blueprints/creed/creed.svg new file mode 100644 index 000000000..a07c3b26e --- /dev/null +++ b/blueprints/creed/creed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/creed/docker-compose.yml b/blueprints/creed/docker-compose.yml new file mode 100644 index 000000000..fb74bfcc5 --- /dev/null +++ b/blueprints/creed/docker-compose.yml @@ -0,0 +1,65 @@ +version: "3.8" +services: + creed: + build: + context: https://github.com/connorhpbrn/creed.git#e9b096d6d68fef6df336338bc9965a94ab62f75e + dockerfile_inline: | + FROM node:20-alpine AS builder + WORKDIR /app + COPY package.json package-lock.json ./ + RUN npm ci + COPY . . + ARG NEXT_PUBLIC_SITE_URL + ARG NEXT_PUBLIC_SUPABASE_URL + ARG NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY + ARG NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY + ARG NEXT_PUBLIC_CONTACT_EMAIL + ARG NEXT_PUBLIC_GITHUB_URL + ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL \ + NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \ + NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=$NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY \ + NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=$NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY \ + NEXT_PUBLIC_CONTACT_EMAIL=$NEXT_PUBLIC_CONTACT_EMAIL \ + NEXT_PUBLIC_GITHUB_URL=$NEXT_PUBLIC_GITHUB_URL + RUN npm run build + + FROM node:20-alpine AS runner + WORKDIR /app + ENV NODE_ENV=production + COPY --from=builder /app/public ./public + COPY --from=builder /app/.next ./.next + COPY --from=builder /app/node_modules ./node_modules + COPY --from=builder /app/package.json ./package.json + COPY --from=builder /app/next.config.ts ./next.config.ts + EXPOSE 3000 + CMD ["npm", "start"] + args: + NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL} + NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL} + NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY} + NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} + NEXT_PUBLIC_CONTACT_EMAIL: ${NEXT_PUBLIC_CONTACT_EMAIL} + NEXT_PUBLIC_GITHUB_URL: ${NEXT_PUBLIC_GITHUB_URL} + restart: unless-stopped + environment: + - NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL} + - NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL} + - NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=${NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY} + - SUPABASE_SECRET_KEY=${SUPABASE_SECRET_KEY} + - CREED_ENCRYPTION_SECRET=${CREED_ENCRYPTION_SECRET} + - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY} + - NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - OPENROUTER_PLATFORM_KEY=${OPENROUTER_PLATFORM_KEY} + - ANALYSIS_MODEL=${ANALYSIS_MODEL} + - TAB_MODEL=${TAB_MODEL} + - PANEL_MODEL=${PANEL_MODEL} + - RESEND_API_KEY=${RESEND_API_KEY} + - RESEND_FROM_EMAIL=${RESEND_FROM_EMAIL} + - GITHUB_OAUTH_CLIENT_ID=${GITHUB_OAUTH_CLIENT_ID} + - GITHUB_OAUTH_CLIENT_SECRET=${GITHUB_OAUTH_CLIENT_SECRET} + - NEXT_PUBLIC_CONTACT_EMAIL=${NEXT_PUBLIC_CONTACT_EMAIL} + - NEXT_PUBLIC_GITHUB_URL=${NEXT_PUBLIC_GITHUB_URL} + - CREED_CSP_ENFORCE=${CREED_CSP_ENFORCE} + expose: + - 3000 diff --git a/blueprints/creed/meta.json b/blueprints/creed/meta.json new file mode 100644 index 000000000..d73220b09 --- /dev/null +++ b/blueprints/creed/meta.json @@ -0,0 +1,19 @@ +{ + "id": "creed", + "name": "Creed", + "version": "e9b096d", + "description": "One personal context file every AI agent reads before answering and proposes updates to as it learns about you. Requires an external Supabase project (see instructions).", + "logo": "creed.svg", + "links": { + "github": "https://github.com/connorhpbrn/creed", + "website": "https://creed.md", + "docs": "https://creed.md/docs" + }, + "tags": [ + "ai", + "productivity", + "mcp", + "nextjs", + "supabase" + ] +} diff --git a/blueprints/creed/template.toml b/blueprints/creed/template.toml new file mode 100644 index 000000000..5b3b3b220 --- /dev/null +++ b/blueprints/creed/template.toml @@ -0,0 +1,105 @@ +[variables] +main_domain = "${domain}" +creed_encryption_secret = "${base64:32}" + +[config] +env = [ + "NEXT_PUBLIC_SITE_URL=https://${main_domain}", + "NEXT_PUBLIC_SUPABASE_URL=", + "NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=", + "SUPABASE_SECRET_KEY=", + "CREED_ENCRYPTION_SECRET=${creed_encryption_secret}", + "STRIPE_SECRET_KEY=", + "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=", + "STRIPE_WEBHOOK_SECRET=", + "OPENROUTER_PLATFORM_KEY=", + "ANALYSIS_MODEL=", + "TAB_MODEL=", + "PANEL_MODEL=", + "RESEND_API_KEY=", + "RESEND_FROM_EMAIL=Creed ", + "GITHUB_OAUTH_CLIENT_ID=", + "GITHUB_OAUTH_CLIENT_SECRET=", + "NEXT_PUBLIC_CONTACT_EMAIL=", + "NEXT_PUBLIC_GITHUB_URL=https://github.com/connorhpbrn/creed", + "CREED_CSP_ENFORCE=", +] + +[[config.domains]] +serviceName = "creed" +port = 3000 +host = "${main_domain}" + +[[config.mounts]] +filePath = "README.md" +content = """# Creed + +Creed (creed.md) is a personal context file: one Markdown file every AI agent +reads before answering and proposes updates to as it learns about you. This +template builds the app from source (https://github.com/connorhpbrn/creed) +and runs it as a stateless Next.js container — Postgres, auth, and storage +live in Supabase, which is **not** part of this template. + +## Before you deploy + +Creed needs a Supabase project. It is not enough to point it at a plain +Postgres database: the app relies on Supabase Auth, Row Level Security, and +realtime subscriptions, and its schema is applied through the Supabase CLI. + +1. Create a Supabase project — either a hosted one at supabase.com, or your + own self-hosted instance (Dokploy has a separate **Supabase** template you + can deploy for this). +2. On a machine with Node 20+, clone the Creed repo and push its schema to + your project: + ```bash + git clone https://github.com/connorhpbrn/creed.git + cd creed && npm install + supabase link --project-ref + supabase db push + ``` +3. From Project Settings -> API in your Supabase dashboard, copy the + project URL, the publishable (anon) key, and the secret (service_role) + key. + +## Required environment variables + +Fill these in on the **Environment** tab before the first deploy, then +redeploy: + +- `NEXT_PUBLIC_SUPABASE_URL`: your Supabase project URL. +- `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`: the Supabase anon/publishable key. +- `SUPABASE_SECRET_KEY`: the Supabase service_role/secret key. Server-only, + never exposed to the browser. +- `CREED_ENCRYPTION_SECRET` is generated for you (used to encrypt provider + tokens at rest). +- `NEXT_PUBLIC_SITE_URL` is set to this service's domain automatically. + +The app boots with only these set, but sign-in, AI features, and billing +stay disabled until their own variables are configured. + +## Optional integrations + +- **Stripe** (paid plans): `STRIPE_SECRET_KEY`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`, + `STRIPE_WEBHOOK_SECRET`. Create a webhook endpoint at + `https:///api/stripe/webhook` in the Stripe dashboard for the + signing secret. +- **OpenRouter** (managed AI credits): `OPENROUTER_PLATFORM_KEY` from + https://openrouter.ai/keys. `ANALYSIS_MODEL`, `TAB_MODEL`, `PANEL_MODEL` + override the default model per AI feature and can be left blank. +- **Resend** (Company invite emails): `RESEND_API_KEY` and + `RESEND_FROM_EMAIL` (must be on a domain verified in Resend). +- **GitHub OAuth** (repo sync, not sign-in): create an OAuth App at + https://github.com/settings/developers with callback URL + `https:///auth/github/callback`, then set + `GITHUB_OAUTH_CLIENT_ID` and `GITHUB_OAUTH_CLIENT_SECRET`. +- `CREED_CSP_ENFORCE=1` switches the Content-Security-Policy header from + report-only to enforcing. Leave it unset for at least one deploy cycle so + you can check the browser console for violations first. + +## Notes + +- This template builds the image from the Creed GitHub repository during + deployment; the first deploy can take a few minutes. +- The app itself is stateless — all data lives in Supabase, so there are no + volumes to back up here. +""" diff --git a/blueprints/crowdsec/docker-compose.yml b/blueprints/crowdsec/docker-compose.yml index ae9ab573a..f8c05e8ae 100644 --- a/blueprints/crowdsec/docker-compose.yml +++ b/blueprints/crowdsec/docker-compose.yml @@ -28,4 +28,4 @@ services: restart: unless-stopped volumes: crowdsec-db: - crowdsec-config: \ No newline at end of file + crowdsec-config: diff --git a/blueprints/cup/docker-compose.yml b/blueprints/cup/docker-compose.yml index 6775151a6..8677cf0d7 100644 --- a/blueprints/cup/docker-compose.yml +++ b/blueprints/cup/docker-compose.yml @@ -5,7 +5,7 @@ services: image: ghcr.io/sergi0g/cup:latest restart: unless-stopped command: serve - ports: + expose: - "8000" volumes: - /var/run/docker.sock:/var/run/docker.sock diff --git a/blueprints/cup/template.toml b/blueprints/cup/template.toml index 60cfd521f..ee6e475cd 100644 --- a/blueprints/cup/template.toml +++ b/blueprints/cup/template.toml @@ -2,16 +2,10 @@ main_domain = "${domain}" [config] - +mounts = [] [[config.domains]] serviceName = "cup" port = 8000 host = "${main_domain}" [config.env] - -[[config.mounts]] -serviceName = "cup" -source = "/var/run/docker.sock" -target = "/var/run/docker.sock" -type = "bind" diff --git a/blueprints/cut/docker-compose.yml b/blueprints/cut/docker-compose.yml index b31578d10..389295a49 100644 --- a/blueprints/cut/docker-compose.yml +++ b/blueprints/cut/docker-compose.yml @@ -11,7 +11,7 @@ services: # 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: + expose: - 3000 healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] diff --git a/blueprints/dashy/docker-compose.yml b/blueprints/dashy/docker-compose.yml index df27c431f..8614ef3af 100644 --- a/blueprints/dashy/docker-compose.yml +++ b/blueprints/dashy/docker-compose.yml @@ -3,9 +3,9 @@ services: dashy: image: lissy93/dashy:latest restart: unless-stopped - ports: + expose: - 8080 volumes: - dashy-config:/app/user-data volumes: - dashy-config: {} \ No newline at end of file + dashy-config: {} diff --git a/blueprints/databasus/docker-compose.yml b/blueprints/databasus/docker-compose.yml index aafa7744a..b46a337a1 100644 --- a/blueprints/databasus/docker-compose.yml +++ b/blueprints/databasus/docker-compose.yml @@ -1,7 +1,7 @@ services: databasus: image: databasus/databasus:latest - ports: + expose: - "4005" volumes: # Persistent data storage diff --git a/blueprints/datalens/docker-compose.yml b/blueprints/datalens/docker-compose.yml index 94839e04b..c0dd8216a 100644 --- a/blueprints/datalens/docker-compose.yml +++ b/blueprints/datalens/docker-compose.yml @@ -23,7 +23,6 @@ services: - us data-api: - container_name: datalens-data-api image: ghcr.io/datalens-tech/datalens-data-api:0.2192.0 restart: always environment: @@ -43,7 +42,6 @@ services: - pg-compeng pg-us: - container_name: datalens-pg-us image: postgres:16-alpine restart: always environment: @@ -75,8 +73,8 @@ services: datalens: image: ghcr.io/datalens-tech/datalens-ui:0.2601.0 restart: always - ports: - - ${UI_PORT:-8080}:8080 + expose: + - 8080 # web UI — routed through Traefik via the template domain depends_on: - us - control-api diff --git a/blueprints/directory-lister/docker-compose.yml b/blueprints/directory-lister/docker-compose.yml index 0e073c6e7..3c50cbe04 100644 --- a/blueprints/directory-lister/docker-compose.yml +++ b/blueprints/directory-lister/docker-compose.yml @@ -2,8 +2,7 @@ services: directory-lister: image: directorylister/directorylister:latest restart: unless-stopped - ports: - # The internal port of the application. + expose: - 80 volumes: # Mounts a persistent named volume to store directory data. diff --git a/blueprints/directory-lister/template.toml b/blueprints/directory-lister/template.toml index e67deb805..a331be2ad 100644 --- a/blueprints/directory-lister/template.toml +++ b/blueprints/directory-lister/template.toml @@ -2,6 +2,7 @@ app_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "directory-lister" # Must match the service name in docker-compose.yml port = 80 @@ -15,5 +16,3 @@ READMES_FIRST = "false" ZIP_DOWNLOADS = "true" TIMEZONE = "UTC" - -[[config.mounts]] diff --git a/blueprints/directus/docker-compose.yml b/blueprints/directus/docker-compose.yml index ef27d2ba2..a4af6581a 100644 --- a/blueprints/directus/docker-compose.yml +++ b/blueprints/directus/docker-compose.yml @@ -1,7 +1,7 @@ services: directus: image: directus/directus:12.1.1 - ports: + expose: - 8055 volumes: - directus_uploads:/directus/uploads diff --git a/blueprints/discord-tickets/docker-compose.yml b/blueprints/discord-tickets/docker-compose.yml index 62105b3a1..93c62d8de 100644 --- a/blueprints/discord-tickets/docker-compose.yml +++ b/blueprints/discord-tickets/docker-compose.yml @@ -47,4 +47,4 @@ services: volumes: tickets-mysql-data: - tickets-app-data: \ No newline at end of file + tickets-app-data: diff --git a/blueprints/discourse/docker-compose.yml b/blueprints/discourse/docker-compose.yml index d0353e8af..4920374e3 100644 --- a/blueprints/discourse/docker-compose.yml +++ b/blueprints/discourse/docker-compose.yml @@ -54,7 +54,7 @@ services: # DISCOURSE_SMTP_ENABLE_START_TLS: "true" # DISCOURSE_NOTIFICATION_EMAIL: [email protected] # DISCOURSE_DEVELOPER_EMAILS: [email protected] - ports: + expose: - 80 restart: unless-stopped diff --git a/blueprints/dockge/docker-compose.yml b/blueprints/dockge/docker-compose.yml index 8736f2fd5..6b9a17331 100644 --- a/blueprints/dockge/docker-compose.yml +++ b/blueprints/dockge/docker-compose.yml @@ -8,9 +8,8 @@ services: - dockge_stacks:/opt/stacks environment: DOCKGE_STACKS_DIR: /opt/stacks - ports: + expose: - "5001" - volumes: dockge_data: dockge_stacks: diff --git a/blueprints/docling-serve/docker-compose.yml b/blueprints/docling-serve/docker-compose.yml index 28b6b3652..42bcba796 100644 --- a/blueprints/docling-serve/docker-compose.yml +++ b/blueprints/docling-serve/docker-compose.yml @@ -2,7 +2,7 @@ services: docling-serve: image: quay.io/docling-project/docling-serve:latest restart: unless-stopped - ports: + expose: - 5001 environment: - DOCLING_SERVE_ENABLE_UI=1 diff --git a/blueprints/docling-serve/template.toml b/blueprints/docling-serve/template.toml index 99fe492f9..e207c279c 100644 --- a/blueprints/docling-serve/template.toml +++ b/blueprints/docling-serve/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "docling-serve" port = 5001 @@ -11,5 +12,3 @@ host = "${main_domain}" DOCLING_SERVE_ENABLE_UI = "1" DOCLING_SERVE_HOST = "0.0.0.0" DOCLING_SERVE_PORT = "5001" - -[[config.mounts]] diff --git a/blueprints/docmost/docker-compose.yml b/blueprints/docmost/docker-compose.yml index d7efba929..b74bba2e6 100644 --- a/blueprints/docmost/docker-compose.yml +++ b/blueprints/docmost/docker-compose.yml @@ -37,4 +37,4 @@ services: volumes: docmost: db_docmost_data: - redis_docmost_data: \ No newline at end of file + redis_docmost_data: diff --git a/blueprints/documenso/docker-compose.yml b/blueprints/documenso/docker-compose.yml index 2eb563167..273791fdb 100644 --- a/blueprints/documenso/docker-compose.yml +++ b/blueprints/documenso/docker-compose.yml @@ -43,7 +43,7 @@ services: - CERT_INFO_ORGANIZATIONAL_UNIT=${CERT_INFO_ORGANIZATIONAL_UNIT:-IT Department} - CERT_INFO_EMAIL=${CERT_INFO_EMAIL:-admin@example.com} - DOCUMENSO_HOST=${DOCUMENSO_HOST} - ports: + expose: - ${DOCUMENSO_PORT} entrypoint: - /bin/sh diff --git a/blueprints/docuseal/docker-compose.yml b/blueprints/docuseal/docker-compose.yml index f5a180b1c..f976fb251 100644 --- a/blueprints/docuseal/docker-compose.yml +++ b/blueprints/docuseal/docker-compose.yml @@ -35,4 +35,4 @@ services: volumes: docuseal: docuseal-db: - docuseal-redis-data: \ No newline at end of file + docuseal-redis-data: diff --git a/blueprints/domain-locker/template.toml b/blueprints/domain-locker/template.toml index 902d49e2e..bc746f2eb 100644 --- a/blueprints/domain-locker/template.toml +++ b/blueprints/domain-locker/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" pg_password = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "app" port = 3000 @@ -26,8 +27,3 @@ DL_PG_NAME = "domain_locker" # DL_DOMAIN_INFO_API = "" # DL_DOMAIN_SUBS_API = "" # DL_DISABLE_WRITE_METHODS = "false" - -[[config.mounts]] -# Example mount for PostgreSQL persistence (already defined in compose volumes) -# filePath = "/var/lib/postgresql/data" -# content = "" diff --git a/blueprints/dozzle/template.toml b/blueprints/dozzle/template.toml index 9bce33653..a62492af3 100644 --- a/blueprints/dozzle/template.toml +++ b/blueprints/dozzle/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" docker_socket = "/var/run/docker.sock" [config] +mounts = [] [[config.domains]] serviceName = "dozzle" port = 8080 @@ -11,8 +12,3 @@ host = "${main_domain}" [config.env] DOZZLE_USERNAME = "${username}" DOZZLE_PASSWORD = "${password:16}" - -[[config.mounts]] -source = "${docker_socket}" -target = "/var/run/docker.sock" -read_only = true diff --git a/blueprints/dragonfly-db/docker-compose.yml b/blueprints/dragonfly-db/docker-compose.yml index 7feaeb82b..908f140b7 100644 --- a/blueprints/dragonfly-db/docker-compose.yml +++ b/blueprints/dragonfly-db/docker-compose.yml @@ -4,11 +4,11 @@ services: image: 'docker.dragonflydb.io/dragonflydb/dragonfly' ulimits: memlock: -1 - ports: - - "6379:6379" + expose: + - 6379 volumes: - dragonflydata:/data environment: - DFLY_requirepass volumes: - dragonflydata: \ No newline at end of file + dragonflydata: diff --git a/blueprints/drawio/docker-compose.yml b/blueprints/drawio/docker-compose.yml index cdd036d92..8a8921020 100644 --- a/blueprints/drawio/docker-compose.yml +++ b/blueprints/drawio/docker-compose.yml @@ -2,23 +2,21 @@ version: '3' services: plantuml-server: image: plantuml/plantuml-server - ports: + expose: - "8080" - volumes: - fonts_volume:/usr/share/fonts/drawio image-export: image: jgraph/export-server - ports: + expose: - "8000" - volumes: - fonts_volume:/usr/share/fonts/drawio environment: - DRAWIO_BASE_URL=${DRAWIO_BASE_URL} drawio: image: jgraph/drawio:24.7.17 - ports: + expose: - "8080" links: - plantuml-server:plantuml-server @@ -52,4 +50,4 @@ services: DRAWIO_GITLAB_URL: ${DRAWIO_GITLAB_URL} DRAWIO_CLOUD_CONVERT_APIKEY: ${DRAWIO_CLOUD_CONVERT_APIKEY} volumes: - fonts_volume: \ No newline at end of file + fonts_volume: diff --git a/blueprints/drizzle-gateway/docker-compose.yml b/blueprints/drizzle-gateway/docker-compose.yml index 460165764..e8c09ac1b 100644 --- a/blueprints/drizzle-gateway/docker-compose.yml +++ b/blueprints/drizzle-gateway/docker-compose.yml @@ -8,12 +8,6 @@ services: MASTERPASS: ${MASTERPASS} volumes: - drizzle-gateway:/app - networks: - - dokploy-network volumes: drizzle-gateway: - -networks: - dokploy-network: - external: true diff --git a/blueprints/drizzle-gateway/meta.json b/blueprints/drizzle-gateway/meta.json index 7bfd97b41..25d099ce5 100644 --- a/blueprints/drizzle-gateway/meta.json +++ b/blueprints/drizzle-gateway/meta.json @@ -6,8 +6,8 @@ "logo": "drizzle-gateway.svg", "links": { "github": "https://github.com/drizzle-team/drizzle-gateway", - "website": "https://drizzle-team.github.io/", - "docs": "https://drizzle-team.github.io/docs" + "website": "https://gateway.drizzle.team/", + "docs": "https://gateway.drizzle.team/docs/railway" }, "tags": [ "database", diff --git a/blueprints/dumbassets/docker-compose.yml b/blueprints/dumbassets/docker-compose.yml index eda4a3f77..5f96f1dd2 100644 --- a/blueprints/dumbassets/docker-compose.yml +++ b/blueprints/dumbassets/docker-compose.yml @@ -3,7 +3,7 @@ services: dumbassets: image: dumbwareio/dumbassets:latest restart: unless-stopped - ports: + expose: - 3000 volumes: - dumbassets-data:/app/data diff --git a/blueprints/dumbassets/template.toml b/blueprints/dumbassets/template.toml index aafb87b47..5e78c09fb 100644 --- a/blueprints/dumbassets/template.toml +++ b/blueprints/dumbassets/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" default_pin = "${password:4}" [config] +mounts = [] [[config.domains]] serviceName = "dumbassets" port = 3000 @@ -19,9 +20,3 @@ DEMO_MODE = "false" APPRISE_URL = "" CURRENCY_CODE = "USD" CURRENCY_LOCALE = "en-US" - -[[config.mounts]] -serviceName = "dumbassets" -type = "volume" -source = "dumbassets-data" -target = "/app/data" diff --git a/blueprints/dumbbudget/docker-compose.yml b/blueprints/dumbbudget/docker-compose.yml index 1d7607788..41fdbe855 100644 --- a/blueprints/dumbbudget/docker-compose.yml +++ b/blueprints/dumbbudget/docker-compose.yml @@ -3,7 +3,7 @@ services: dumbbudget: image: dumbwareio/dumbbudget:latest restart: unless-stopped - ports: + expose: - 3000 volumes: - dumbbudget-data:/app/data diff --git a/blueprints/dumbbudget/template.toml b/blueprints/dumbbudget/template.toml index 7f5abe24e..3767a1664 100644 --- a/blueprints/dumbbudget/template.toml +++ b/blueprints/dumbbudget/template.toml @@ -4,6 +4,7 @@ main_domain = "${domain}" dumbbudget_pin = "${password:16}" [config] +mounts = [] [[config.domains]] serviceName = "dumbbudget" port = 3000 @@ -20,5 +21,4 @@ ALLOWED_ORIGINS = "${main_domain}" # The named volume 'dumbbudget-data' is defined in the docker-compose.yml. # According to Dokploy's template examples, volumes declared in the compose -# file are automatically managed and do not require a separate entry here. -[[config.mounts]] +# file are automatically managed and do not require a separate entry here. \ No newline at end of file diff --git a/blueprints/dumbdrop/docker-compose.yml b/blueprints/dumbdrop/docker-compose.yml index 993e0b304..f4e2650b6 100644 --- a/blueprints/dumbdrop/docker-compose.yml +++ b/blueprints/dumbdrop/docker-compose.yml @@ -2,7 +2,7 @@ services: dumbdrop: image: dumbwareio/dumbdrop:latest restart: unless-stopped - ports: + expose: - 3000 volumes: - dumbdrop-uploads:/app/uploads diff --git a/blueprints/dumbdrop/template.toml b/blueprints/dumbdrop/template.toml index fa05a3244..517afcbfd 100644 --- a/blueprints/dumbdrop/template.toml +++ b/blueprints/dumbdrop/template.toml @@ -2,6 +2,7 @@ main_domain = "https://${domain}" [config] +mounts = [] [[config.domains]] serviceName = "dumbdrop" port = 3000 @@ -20,5 +21,3 @@ APPRISE_URL = "" APPRISE_MESSAGE = "New file uploaded {filename} ({size}), Storage used {storage}" APPRISE_SIZE_UNIT = "Auto" ALLOWED_EXTENSIONS = "" - -[[config.mounts]] diff --git a/blueprints/dumbpad/docker-compose.yml b/blueprints/dumbpad/docker-compose.yml index ec9487c59..ea536cb38 100644 --- a/blueprints/dumbpad/docker-compose.yml +++ b/blueprints/dumbpad/docker-compose.yml @@ -3,7 +3,7 @@ services: dumbpad: image: dumbwareio/dumbpad:latest restart: unless-stopped - ports: + expose: - 3000 volumes: - dumbpad-data:/app/data diff --git a/blueprints/dumbpad/template.toml b/blueprints/dumbpad/template.toml index 9e130795f..86dbd3e25 100644 --- a/blueprints/dumbpad/template.toml +++ b/blueprints/dumbpad/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "dumbpad" port = 3000 @@ -17,5 +18,3 @@ LOCKOUT_TIME = "15" MAX_ATTEMPTS = "5" COOKIE_MAX_AGE = "24" PAGE_HISTORY_COOKIE_AGE = "365" - -[[config.mounts]] diff --git a/blueprints/elastic-search/docker-compose.yml b/blueprints/elastic-search/docker-compose.yml index 929006ff1..4ae349e4b 100644 --- a/blueprints/elastic-search/docker-compose.yml +++ b/blueprints/elastic-search/docker-compose.yml @@ -3,7 +3,6 @@ version: '3.8' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.10.2 - container_name: elasticsearch environment: - discovery.type=single-node - xpack.security.enabled=false @@ -13,17 +12,16 @@ services: memlock: soft: -1 hard: -1 - ports: + expose: - "9200" volumes: - es_data:/usr/share/elasticsearch/data kibana: image: docker.elastic.co/kibana/kibana:8.10.2 - container_name: kibana environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 - ports: + expose: - "5601" depends_on: - elasticsearch @@ -31,4 +29,4 @@ services: volumes: es_data: driver: local - \ No newline at end of file + diff --git a/blueprints/emby/docker-compose.yml b/blueprints/emby/docker-compose.yml index 1b7cb15aa..32b54f189 100644 --- a/blueprints/emby/docker-compose.yml +++ b/blueprints/emby/docker-compose.yml @@ -12,9 +12,9 @@ services: - /emby/programdata:/config # Configuration directory - /emby/series:/mnt/share1 # Media directory - /emby/movies:/mnt/share2 # Media directory - ports: + expose: - 8096 # HTTP port - 8920 # HTTPS port devices: - /dev/dri:/dev/dri # VAAPI/NVDEC/NVENC render nodes - restart: on-failure \ No newline at end of file + restart: on-failure diff --git a/blueprints/emqx/docker-compose.yml b/blueprints/emqx/docker-compose.yml index 4b11f0109..bde86aa7b 100644 --- a/blueprints/emqx/docker-compose.yml +++ b/blueprints/emqx/docker-compose.yml @@ -21,10 +21,6 @@ services: volumes: - emqx_data:/opt/emqx/data - emqx_log:/opt/emqx/log - networks: - dokploy-network: - aliases: - - emqx-service restart: unless-stopped healthcheck: test: ["CMD", "/opt/emqx/bin/emqx_ctl", "status"] @@ -52,7 +48,3 @@ services: volumes: emqx_data: emqx_log: - -networks: - dokploy-network: - external: true diff --git a/blueprints/enshrouded/docker-compose.yml b/blueprints/enshrouded/docker-compose.yml index 614dd36e3..45cdf23d0 100644 --- a/blueprints/enshrouded/docker-compose.yml +++ b/blueprints/enshrouded/docker-compose.yml @@ -1,7 +1,7 @@ +# dokploy: allow-host-ports — Enshrouded game clients connect over raw UDP (15637 game, 27015 Steam query); Traefik cannot route UDP game traffic. services: enshrouded: image: mornedhels/enshrouded-server:latest - container_name: enshrouded hostname: enshrouded restart: unless-stopped stop_grace_period: 90s @@ -22,4 +22,4 @@ services: - PGID=4711 volumes: - enshrouded-persistent-data: \ No newline at end of file + enshrouded-persistent-data: diff --git a/blueprints/erpnext-v16/docker-compose.yml b/blueprints/erpnext-v16/docker-compose.yml index 3201b4d77..0f5c7c735 100644 --- a/blueprints/erpnext-v16/docker-compose.yml +++ b/blueprints/erpnext-v16/docker-compose.yml @@ -298,4 +298,4 @@ volumes: driver_opts: type: "${SITE_VOLUME_TYPE}" o: "${SITE_VOLUME_OPTS}" - device: "${SITE_VOLUME_DEV}" \ No newline at end of file + device: "${SITE_VOLUME_DEV}" diff --git a/blueprints/erpnext/docker-compose.yml b/blueprints/erpnext/docker-compose.yml index 28cd8f6ab..fc56c8a00 100644 --- a/blueprints/erpnext/docker-compose.yml +++ b/blueprints/erpnext/docker-compose.yml @@ -10,8 +10,6 @@ services: <<: *custom_image volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -42,8 +40,6 @@ services: volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: @@ -63,8 +59,6 @@ services: - default volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -87,8 +81,6 @@ services: - long volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -111,8 +103,6 @@ services: - short volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -145,8 +135,6 @@ services: required: true volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network websocket: <<: *custom_image @@ -167,8 +155,6 @@ services: required: true volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network configurator: <<: *custom_image @@ -197,8 +183,6 @@ services: REGENERATE_APPS_TXT: "${REGENERATE_APPS_TXT:-0}" volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network create-site: <<: *custom_image @@ -237,8 +221,6 @@ services: DB_PORT: "${DB_PORT:-3306}" DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} INSTALL_APP_ARGS: ${INSTALL_APP_ARGS} - networks: - - bench-network migration: <<: *custom_image @@ -258,8 +240,6 @@ services: bench --site all set-config -p pause_scheduler 0; volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network db: image: mariadb:10.6 @@ -282,8 +262,6 @@ services: - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} volumes: - db-data:/var/lib/mysql - networks: - - bench-network redis-cache: deploy: @@ -292,8 +270,6 @@ services: image: redis:6.2-alpine volumes: - redis-cache-data:/data - networks: - - bench-network healthcheck: test: - CMD @@ -310,8 +286,6 @@ services: image: redis:6.2-alpine volumes: - redis-queue-data:/data - networks: - - bench-network healthcheck: test: - CMD @@ -328,8 +302,6 @@ services: image: redis:6.2-alpine volumes: - redis-socketio-data:/data - networks: - - bench-network healthcheck: test: - CMD @@ -349,6 +321,3 @@ volumes: type: "${SITE_VOLUME_TYPE}" o: "${SITE_VOLUME_OPTS}" device: "${SITE_VOLUME_DEV}" - -networks: - bench-network: \ No newline at end of file diff --git a/blueprints/evershop/docker-compose.yml b/blueprints/evershop/docker-compose.yml index d0a7fcd01..71832e555 100644 --- a/blueprints/evershop/docker-compose.yml +++ b/blueprints/evershop/docker-compose.yml @@ -12,9 +12,8 @@ services: DB_NAME: ${DB_NAME} depends_on: - database - ports: + expose: - 3000 - database: image: postgres:16 restart: unless-stopped @@ -24,8 +23,7 @@ services: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USER} POSTGRES_DB: ${DB_NAME} - ports: + expose: - ${DB_PORT} - volumes: postgres-data: diff --git a/blueprints/evolutionapi/docker-compose.yml b/blueprints/evolutionapi/docker-compose.yml index 5bdd30751..adb37b6ac 100644 --- a/blueprints/evolutionapi/docker-compose.yml +++ b/blueprints/evolutionapi/docker-compose.yml @@ -57,4 +57,4 @@ services: volumes: evolution-instances: evolution-postgres-data: - evolution-redis-data: \ No newline at end of file + evolution-redis-data: diff --git a/blueprints/ezbookkeeping/template.toml b/blueprints/ezbookkeeping/template.toml index c41759ee7..5686867ce 100644 --- a/blueprints/ezbookkeeping/template.toml +++ b/blueprints/ezbookkeeping/template.toml @@ -7,6 +7,7 @@ root_pass = "${password:32}" secret_key = "${password:64}" [config] +mounts = [] [[config.domains]] serviceName = "ezbookkeeping" port = 8080 @@ -27,5 +28,3 @@ EBK_DATABASE_PASSWD = "${db_pass}" EBK_LOG_MODE = "file" # Security secret key used for application protection EBK_SECURITY_SECRET_KEY = "${secret_key}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/filebrowser/docker-compose.yml b/blueprints/filebrowser/docker-compose.yml index 10c119091..0a5d4c08c 100644 --- a/blueprints/filebrowser/docker-compose.yml +++ b/blueprints/filebrowser/docker-compose.yml @@ -11,4 +11,4 @@ services: volumes: filebrowser-data: filebrowser-config: - \ No newline at end of file + diff --git a/blueprints/filegator/docker-compose.yml b/blueprints/filegator/docker-compose.yml index f2f733516..bce6fdcb9 100644 --- a/blueprints/filegator/docker-compose.yml +++ b/blueprints/filegator/docker-compose.yml @@ -5,9 +5,8 @@ services: volumes: - filegator_repository:/var/www/filegator/repository - filegator_private:/var/www/filegator/private - ports: + expose: - "8080" - volumes: filegator_repository: filegator_private: diff --git a/blueprints/filestash/docker-compose.yml b/blueprints/filestash/docker-compose.yml index 6659fc4a6..c0c98a270 100644 --- a/blueprints/filestash/docker-compose.yml +++ b/blueprints/filestash/docker-compose.yml @@ -10,7 +10,7 @@ services: - OFFICE_URL=${OFFICE_URL} - OFFICE_FILESTASH_URL=${OFFICE_FILESTASH_URL} - OFFICE_REWRITE_URL=${OFFICE_REWRITE_URL} - ports: + expose: - 8334 volumes: - filestash:/app/data/state/ @@ -29,8 +29,7 @@ services: curl -o /usr/share/coolwsd/browser/dist/branding-desktop.css https://gist.githubusercontent.com/mickael-kerjean/bc1f57cd312cf04731d30185cc4e7ba2/raw/d706dcdf23c21441e5af289d871b33defc2770ea/destop.css /bin/su -s /bin/bash -c '/start-collabora-online.sh' cool user: root - ports: + expose: - 9980 - volumes: - filestash: \ No newline at end of file + filestash: diff --git a/blueprints/firecrawl/docker-compose.yml b/blueprints/firecrawl/docker-compose.yml index d7622f7a5..1a36c8f71 100644 --- a/blueprints/firecrawl/docker-compose.yml +++ b/blueprints/firecrawl/docker-compose.yml @@ -56,7 +56,7 @@ services: api: <<: *common-service restart: unless-stopped - ports: + expose: - "3002" environment: <<: *common-env @@ -126,4 +126,4 @@ services: retries: 10 volumes: - nuq_pg_data: \ No newline at end of file + nuq_pg_data: diff --git a/blueprints/fivem/docker-compose.yml b/blueprints/fivem/docker-compose.yml index 162239bff..61d676f8c 100644 --- a/blueprints/fivem/docker-compose.yml +++ b/blueprints/fivem/docker-compose.yml @@ -1,5 +1,6 @@ +# dokploy: allow-host-ports — FiveM game clients connect over raw TCP/UDP on 30120; Traefik cannot route game traffic. # docker-compose.yml -# +# # IMPORTANT: FiveM Template - Two Deployment Modes # # MODE 1: Standard FiveM Server @@ -38,4 +39,4 @@ services: volumes: fivem_config: - fivem_txdata: \ No newline at end of file + fivem_txdata: diff --git a/blueprints/flagsmith/docker-compose.yml b/blueprints/flagsmith/docker-compose.yml index 126051f79..99f66db0d 100644 --- a/blueprints/flagsmith/docker-compose.yml +++ b/blueprints/flagsmith/docker-compose.yml @@ -54,7 +54,7 @@ services: # EMAIL_HOST_PASSWORD: smtp_account_password # EMAIL_PORT: 587 # optional # EMAIL_USE_TLS: 'true' # optional - ports: + expose: - 8000 depends_on: postgres: @@ -69,7 +69,7 @@ services: USE_POSTGRES_FOR_ANALYTICS: "true" DJANGO_ALLOWED_HOSTS: "*" PROMETHEUS_ENABLED: "true" - ports: + expose: - 8000 depends_on: - flagsmith diff --git a/blueprints/flaresolverr/docker-compose.yml b/blueprints/flaresolverr/docker-compose.yml index 318141336..d8e61526c 100644 --- a/blueprints/flaresolverr/docker-compose.yml +++ b/blueprints/flaresolverr/docker-compose.yml @@ -2,6 +2,6 @@ version: "3.8" services: flaresolverr: image: ghcr.io/flaresolverr/flaresolverr:latest - ports: + expose: - 8191 restart: unless-stopped diff --git a/blueprints/flaresolverr/template.toml b/blueprints/flaresolverr/template.toml index 4b5f03d3a..9187c5af3 100644 --- a/blueprints/flaresolverr/template.toml +++ b/blueprints/flaresolverr/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "flaresolverr" port = 8191 @@ -12,5 +13,3 @@ LOG_LEVEL = "info" LOG_HTML = "false" CAPTCHA_SOLVER = "none" TZ = "Europe/London" - -[[config.mounts]] diff --git a/blueprints/flarum/docker-compose.yml b/blueprints/flarum/docker-compose.yml index 62595ec9b..80a126647 100644 --- a/blueprints/flarum/docker-compose.yml +++ b/blueprints/flarum/docker-compose.yml @@ -3,7 +3,7 @@ services: flarum: image: crazymax/flarum:latest restart: unless-stopped - ports: + expose: - 8000 environment: - FLARUM_BASE_URL=https://${FLARUM_DOMAIN} diff --git a/blueprints/flatnotes-totp/template.toml b/blueprints/flatnotes-totp/template.toml index 8ce4bc9cf..e414d648c 100644 --- a/blueprints/flatnotes-totp/template.toml +++ b/blueprints/flatnotes-totp/template.toml @@ -6,6 +6,7 @@ secret_key = "${password:32}" totp_key = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "flatnotes" port = 8080 @@ -22,7 +23,3 @@ FLATNOTES_SECRET_KEY = "${secret_key}" FLATNOTES_TOTP_KEY = "${totp_key}" FLATNOTES_SESSION_EXPIRY_DAYS = "30" FLATNOTES_PATH_PREFIX = "" - -[[config.mounts]] -name = "flatnotes-data" -mountPath = "/data" diff --git a/blueprints/flatnotes/template.toml b/blueprints/flatnotes/template.toml index 6749da36d..2f5a961ba 100644 --- a/blueprints/flatnotes/template.toml +++ b/blueprints/flatnotes/template.toml @@ -5,6 +5,7 @@ password = "${password:16}" secret_key = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "flatnotes" port = 8080 @@ -26,7 +27,3 @@ FLATNOTES_QUICK_ACCESS_TITLE = "RECENTLY MODIFIED" FLATNOTES_QUICK_ACCESS_TERM = "*" FLATNOTES_QUICK_ACCESS_SORT = "lastModified" FLATNOTES_QUICK_ACCESS_LIMIT = "4" - -[[config.mounts]] -name = "flatnotes-data" -mountPath = "/data" diff --git a/blueprints/flowise/template.toml b/blueprints/flowise/template.toml index 56986cd3c..d3c5a7cf9 100644 --- a/blueprints/flowise/template.toml +++ b/blueprints/flowise/template.toml @@ -5,6 +5,7 @@ jwt_refresh_secret = "${password:32}" express_secret = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "flowise" port = 3000 @@ -14,11 +15,3 @@ host = "${main_domain}" JWT_AUTH_TOKEN_SECRET = "${jwt_secret}" JWT_REFRESH_TOKEN_SECRET = "${jwt_refresh_secret}" EXPRESS_SESSION_SECRET = "${express_secret}" - -[[config.mounts]] -name = "flowise_data" -mountPath = "/root/.flowise" - -[[config.mounts]] -name = "redis_data" -mountPath = "/data" diff --git a/blueprints/fluxer/docker-compose.yml b/blueprints/fluxer/docker-compose.yml new file mode 100644 index 000000000..1ea9d0199 --- /dev/null +++ b/blueprints/fluxer/docker-compose.yml @@ -0,0 +1,459 @@ +x-fluxer-env: &fluxer-env + FLUXER_ENV: production + NODE_ENV: production + FLUXER_SELF_HOSTED: "true" + FLUXER_BASE_DOMAIN: ${FLUXER_DOMAIN} + FLUXER_PUBLIC_SCHEME: ${FLUXER_PUBLIC_SCHEME:-https} + FLUXER_PUBLIC_PORT: ${FLUXER_PUBLIC_PORT:-443} + FLUXER_TRUST_CLIENT_IP_HEADER: "true" + FLUXER_CLIENT_IP_HEADER_NAME: x-forwarded-for + + FLUXER_DATABASE_BACKEND: postgres + FLUXER_POSTGRES_HOST: postgres + FLUXER_POSTGRES_PORT: "5432" + FLUXER_POSTGRES_DATABASE: fluxer + FLUXER_POSTGRES_USERNAME: fluxer + FLUXER_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + FLUXER_POSTGRES_SSL: "false" + + FLUXER_KV_URL: redis://valkey:6379/0 + FLUXER_NATS_URL: nats://nats:4222 + FLUXER_NATS_JETSTREAM_URL: nats://nats:4222 + FLUXER_SVC_NATS_URL: nats://nats:4222 + FLUXER_SVC_SHARD_COUNT: "1" + + FLUXER_SEARCH_ENGINE: meilisearch + FLUXER_SEARCH_URL: http://meilisearch:7700 + FLUXER_SEARCH_API_KEY: ${MEILI_MASTER_KEY} + + FLUXER_S3_ENDPOINT: http://seaweedfs:8333 + FLUXER_S3_PUBLIC_ENDPOINT: http://seaweedfs:8333 + FLUXER_S3_REGION: us-east-1 + FLUXER_S3_ACCESS_KEY_ID: ${FLUXER_S3_ACCESS_KEY} + FLUXER_S3_SECRET_ACCESS_KEY: ${FLUXER_S3_SECRET_KEY} + FLUXER_S3_FORCE_PATH_STYLE: "true" + FLUXER_S3_BUCKET_CDN: fluxer + FLUXER_S3_BUCKET_UPLOADS: fluxer-uploads + FLUXER_S3_BUCKET_DOWNLOADS: fluxer-downloads + FLUXER_S3_BUCKET_REPORTS: fluxer-reports + FLUXER_S3_BUCKET_HARVESTS: fluxer-harvests + AWS_ACCESS_KEY_ID: ${FLUXER_S3_ACCESS_KEY} + AWS_SECRET_ACCESS_KEY: ${FLUXER_S3_SECRET_KEY} + AWS_DEFAULT_REGION: us-east-1 + AWS_EC2_METADATA_DISABLED: "true" + + FLUXER_LIVEKIT_ENABLED: "true" + FLUXER_LIVEKIT_API_KEY: ${LIVEKIT_API_KEY} + FLUXER_LIVEKIT_API_SECRET: ${LIVEKIT_API_SECRET} + FLUXER_LIVEKIT_WEBHOOK_URL: http://api:8080/webhooks/livekit + + FLUXER_EMAIL_ENABLED: ${FLUXER_EMAIL_ENABLED:-false} + FLUXER_EMAIL_PROVIDER: ${FLUXER_EMAIL_PROVIDER:-none} + FLUXER_EMAIL_FROM_EMAIL: ${FLUXER_EMAIL_FROM_EMAIL:-noreply@localhost} + FLUXER_EMAIL_FROM_NAME: ${FLUXER_EMAIL_FROM_NAME:-Fluxer} + FLUXER_EMAIL_SMTP_HOST: ${FLUXER_EMAIL_SMTP_HOST:-} + FLUXER_EMAIL_SMTP_PORT: ${FLUXER_EMAIL_SMTP_PORT:-587} + FLUXER_EMAIL_SMTP_USERNAME: ${FLUXER_EMAIL_SMTP_USERNAME:-} + FLUXER_EMAIL_SMTP_PASSWORD: ${FLUXER_EMAIL_SMTP_PASSWORD:-} + FLUXER_EMAIL_SMTP_SECURE: ${FLUXER_EMAIL_SMTP_SECURE:-true} + + FLUXER_SMS_ENABLED: "false" + FLUXER_CAPTCHA_ENABLED: ${FLUXER_CAPTCHA_ENABLED:-false} + FLUXER_CAPTCHA_PROVIDER: ${FLUXER_CAPTCHA_PROVIDER:-none} + FLUXER_STRIPE_ENABLED: "false" + FLUXER_NCMEC_ENABLED: "false" + FLUXER_CLAMAV_ENABLED: "false" + FLUXER_DISCOVERY_ENABLED: ${FLUXER_DISCOVERY_ENABLED:-true} + + FLUXER_SUDO_MODE_SECRET: ${FLUXER_SUDO_MODE_SECRET} + FLUXER_CONNECTION_INITIATION_SECRET: ${FLUXER_CONNECTION_INITIATION_SECRET} + FLUXER_VAPID_PUBLIC_KEY: ${FLUXER_VAPID_PUBLIC_KEY} + FLUXER_VAPID_PRIVATE_KEY: ${FLUXER_VAPID_PRIVATE_KEY} + FLUXER_VAPID_EMAIL: ${FLUXER_VAPID_EMAIL} + FLUXER_GATEWAY_RPC_AUTH_TOKEN: ${FLUXER_GATEWAY_RPC_AUTH_TOKEN} + FLUXER_MEDIA_PROXY_SECRET_KEY: ${FLUXER_MEDIA_PROXY_SECRET_KEY} + FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64: ${FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64} + FLUXER_ADMIN_SECRET_KEY_BASE: ${FLUXER_ADMIN_SECRET_KEY_BASE} + FLUXER_ADMIN_OAUTH_CLIENT_SECRET: ${FLUXER_ADMIN_OAUTH_CLIENT_SECRET} + + FLUXER_INTERNAL_API_ENDPOINT: http://api:8080 + FLUXER_INTERNAL_GATEWAY_ENDPOINT: http://gateway:8080 + FLUXER_INTERNAL_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080 + FLUXER_MARKETING_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN} + FLUXER_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080 + FLUXER_MEDIA_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/media + FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/media + + FLUXER_PASSKEY_RP_NAME: ${FLUXER_PASSKEY_RP_NAME:-Fluxer} + FLUXER_PASSKEY_RP_ID: ${FLUXER_PASSKEY_RP_ID:-${FLUXER_DOMAIN}} + FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS: ${FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS:-https://${FLUXER_DOMAIN}} + +x-fluxer-service: &fluxer-service + restart: unless-stopped + +services: + caddy: + image: caddy:2.10-alpine + restart: unless-stopped + ports: + - "80" + configs: + - source: caddyfile + target: /etc/caddy/Caddyfile + volumes: + - caddy-data:/data + - caddy-config:/config + depends_on: [api, gateway, media-proxy, static-proxy, admin] + + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: fluxer + POSTGRES_USER: fluxer + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U fluxer -d fluxer"] + interval: 10s + timeout: 5s + retries: 10 + + valkey: + image: valkey/valkey:8.1-alpine + restart: unless-stopped + command: ["valkey-server", "--save", "", "--appendonly", "no"] + healthcheck: + test: ["CMD", "valkey-cli", "ping"] + interval: 10s + timeout: 5s + retries: 10 + + nats: + image: nats:2.14-alpine + restart: unless-stopped + command: ["-js", "-sd", "/data", "-m", "8222"] + volumes: + - nats-data:/data + + meilisearch: + image: getmeili/meilisearch:v1.12 + restart: unless-stopped + environment: + MEILI_ENV: production + MEILI_NO_ANALYTICS: "true" + MEILI_MASTER_KEY: ${MEILI_MASTER_KEY} + volumes: + - meilisearch-data:/meili_data + + seaweedfs: + image: chrislusf/seaweedfs:4.34 + restart: unless-stopped + command: ["server", "-s3", "-dir=/data"] + volumes: + - seaweedfs-data:/data + + gifs: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-gifs:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_NAME: gifs + FLUXER_SVC_MODE: router + FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/media + depends_on: + nats: {condition: service_started} + + gifs-shard: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-gifs:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_NAME: gifs + FLUXER_SVC_MODE: shard + FLUXER_SVC_SHARD_ID: "0" + FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/media + depends_on: + nats: {condition: service_started} + + seaweedfs-init: + image: chrislusf/seaweedfs:4.34 + depends_on: [seaweedfs] + restart: "no" + entrypoint: + - /bin/sh + - -c + - | + echo "Waiting for SeaweedFS to initialise..." + sleep 12 + echo "Creating S3 buckets..." + printf 's3.bucket.create -name fluxer\ns3.bucket.create -name fluxer-uploads\ns3.bucket.create -name fluxer-downloads\ns3.bucket.create -name fluxer-reports\ns3.bucket.create -name fluxer-harvests\nexit\n' | weed shell -master=seaweedfs:9333 2>&1 & + WEED_PID=$$! + sleep 15 + kill $$WEED_PID 2>/devnull || true + echo "buckets ready" + + livekit: + image: livekit/livekit-server:v1.12.0 + restart: unless-stopped + command: ["--config", "/etc/livekit.yaml"] + environment: + LIVEKIT_KEYS: "${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}" + configs: + - source: livekit_config + target: /etc/livekit.yaml + ports: + - "7881" + - "7882/udp" + + api: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-api:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_API_PORT: "8080" + FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED: "true" + healthcheck: + test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:8080/_health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""] + interval: 10s + timeout: 5s + retries: 30 + start_period: 90s + depends_on: + postgres: {condition: service_healthy} + valkey: {condition: service_healthy} + nats: {condition: service_started} + meilisearch: {condition: service_started} + seaweedfs-init: {condition: service_completed_successfully} + snowflakes: {condition: service_started} + snowflakes-shard: {condition: service_started} + messages: {condition: service_started} + messages-shard: {condition: service_started} + users: {condition: service_started} + users-shard: {condition: service_started} + + worker: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-api:${FLUXER_IMAGE_TAG:-v1} + working_dir: /usr/src/app/fluxer_api + command: ["./node_modules/.bin/tsx", "src/WorkerEntrypoint.ts"] + environment: + <<: *fluxer-env + FLUXER_API_WORKER_MODE: all_lanes + FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER: "true" + FLUXER_API_WORKER_ENABLE_VOICE_RECONCILIATION: "true" + depends_on: + postgres: {condition: service_healthy} + valkey: {condition: service_healthy} + nats: {condition: service_started} + seaweedfs-init: {condition: service_completed_successfully} + snowflakes-shard: {condition: service_started} + messages-shard: {condition: service_started} + users-shard: {condition: service_started} + + gateway: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-gateway:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_GATEWAY_PORT: "8080" + FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/media + FLUXER_GATEWAY_STATIC_CDN_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN} + FLUXER_GATEWAY_LOGGER_LEVEL: info + depends_on: + nats: {condition: service_started} + valkey: {condition: service_healthy} + + media-proxy: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-media-proxy:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_MEDIA_PROXY_HOST: 0.0.0.0 + FLUXER_MEDIA_PROXY_PORT: "8080" + FLUXER_MEDIA_PROXY_MODE: upload + FLUXER_MEDIA_PROXY_STORAGE_BACKEND: s3 + depends_on: + seaweedfs-init: {condition: service_completed_successfully} + nats: {condition: service_started} + + static-proxy: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-static:${FLUXER_IMAGE_TAG:-v1} + + app-proxy: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-app-proxy-self-hosted:${FLUXER_IMAGE_TAG:-v1} + environment: + FLUXER_APP_PROXY_HOST: 0.0.0.0 + FLUXER_APP_PROXY_PORT: "8080" + DISCOVERY_UPSTREAM_URL: http://caddy:8088/api/.well-known/fluxer + PUBLIC_BOOTSTRAP_API_ENDPOINT: /api + PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/api + depends_on: + api: {condition: service_healthy} + caddy: {condition: service_started} + + snowflakes: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-snowflakes:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: router + depends_on: + nats: {condition: service_started} + + snowflakes-shard: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-snowflakes:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: shard + FLUXER_SVC_SHARD_ID: "0" + depends_on: + nats: {condition: service_started} + + users: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-users:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: router + depends_on: + nats: {condition: service_started} + + users-shard: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-users:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: shard + FLUXER_SVC_SHARD_ID: "0" + depends_on: + nats: {condition: service_started} + + messages: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-messages:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: router + depends_on: + nats: {condition: service_started} + + messages-shard: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-messages:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: shard + FLUXER_SVC_SHARD_ID: "0" + depends_on: + nats: {condition: service_started} + postgres: {condition: service_healthy} + + unfurl: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-unfurl:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: router + depends_on: + nats: {condition: service_started} + + unfurl-shard: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-unfurl:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_SVC_MODE: shard + FLUXER_SVC_SHARD_ID: "0" + depends_on: + nats: {condition: service_started} + + admin: + <<: *fluxer-service + image: ${FLUXER_REGISTRY:-ghcr.io/fluxerapp}/fluxer-admin:${FLUXER_IMAGE_TAG:-v1} + environment: + <<: *fluxer-env + FLUXER_ADMIN_HOST: 0.0.0.0 + FLUXER_ADMIN_PORT: "8080" + FLUXER_ADMIN_BASE_PATH: /admin + FLUXER_API_ENDPOINT: http://api:8080 + FLUXER_ADMIN_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/admin + FLUXER_APP_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN} + FLUXER_MEDIA_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/media + FLUXER_STATIC_CDN_ENDPOINT: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN} + FLUXER_ADMIN_OAUTH_REDIRECT_URI: ${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}/admin/oauth2_callback + depends_on: + api: {condition: service_healthy} + +volumes: + caddy-data: + caddy-config: + postgres-data: + nats-data: + meilisearch-data: + seaweedfs-data: + +configs: + caddyfile: + content: | + :80 { + encode zstd gzip + handle /.well-known/* { + reverse_proxy api:8080 + } + handle_path /api/* { + reverse_proxy api:8080 + } + handle /gateway { + rewrite * / + reverse_proxy gateway:8080 + } + handle_path /gateway/* { + reverse_proxy gateway:8080 + } + handle_path /media/* { + reverse_proxy media-proxy:8080 + } + handle_path /livekit/* { + reverse_proxy livekit:7880 + } + handle /admin { + rewrite * / + reverse_proxy admin:8080 + } + handle_path /admin/* { + reverse_proxy admin:8080 + } + @staticAssets path /fonts/* /web/* /emoji/* /libs/* /avatars/* /badges/* /desktop/* /embeds/* + handle @staticAssets { + reverse_proxy static-proxy:8080 + } + handle { + reverse_proxy app-proxy:8080 + } + } + :8088 { + handle_path /api/* { + reverse_proxy api:8080 + } + } + + livekit_config: + content: | + port: 7880 + log_level: info + rtc: + tcp_port: 7881 + udp_port: 7882 + use_external_ip: true + stun_servers: + - stun.l.google.com:19302 + - stun1.l.google.com:19302 + webhook: + api_key: ${LIVEKIT_API_KEY} + urls: + - http://api:8080/webhooks/livekit \ No newline at end of file diff --git a/blueprints/fluxer/logo.svg b/blueprints/fluxer/logo.svg new file mode 100644 index 000000000..0c18745ba --- /dev/null +++ b/blueprints/fluxer/logo.svg @@ -0,0 +1 @@ +Fluxer diff --git a/blueprints/fluxer/meta.json b/blueprints/fluxer/meta.json new file mode 100644 index 000000000..498f7ccbb --- /dev/null +++ b/blueprints/fluxer/meta.json @@ -0,0 +1,17 @@ +{ + "id": "fluxer", + "name": "Fluxer", + "version": "1.0.0", + "description": "Fluxer is a self-hosted, scalable chat and community platform.", + "logo": "logo.svg", + "links": { + "github": "https://github.com/fluxerapp", + "website": "https://fluxer.app", + "docs": "https://github.com/fluxerapp" + }, + "tags": [ + "chat", + "community", + "communication" + ] +} \ No newline at end of file diff --git a/blueprints/fluxer/template.toml b/blueprints/fluxer/template.toml new file mode 100644 index 000000000..8018ef122 --- /dev/null +++ b/blueprints/fluxer/template.toml @@ -0,0 +1,107 @@ +[variables] +FLUXER_DOMAIN = "${domain}" +FLUXER_PUBLIC_SCHEME = "http" +FLUXER_PUBLIC_PORT = "80" + +POSTGRES_PASSWORD = "${hash:64}" +MEILI_MASTER_KEY = "${hash:64}" +FLUXER_S3_ACCESS_KEY = "fluxer" +FLUXER_S3_SECRET_KEY = "${hash:64}" + +FLUXER_SUDO_MODE_SECRET = "${hash:64}" +FLUXER_CONNECTION_INITIATION_SECRET = "${hash:64}" +FLUXER_GATEWAY_RPC_AUTH_TOKEN = "${hash:64}" +FLUXER_MEDIA_PROXY_SECRET_KEY = "${hash:64}" +FLUXER_ADMIN_SECRET_KEY_BASE = "${hash:64}" +FLUXER_ADMIN_OAUTH_CLIENT_SECRET = "${hash:64}" + +FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64 = "${base64:32}" + +FLUXER_VAPID_PUBLIC_KEY = "${password:64}" +FLUXER_VAPID_PRIVATE_KEY = "${password:32}" +FLUXER_VAPID_EMAIL = "admin@example.com" + +LIVEKIT_API_KEY = "fluxer" +LIVEKIT_API_SECRET = "${hash:64}" + +FLUXER_PASSKEY_RP_NAME = "Fluxer" +FLUXER_PASSKEY_RP_ID = "${FLUXER_DOMAIN}" +FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS = "http://${FLUXER_DOMAIN}" + +FLUXER_EMAIL_ENABLED = "false" +FLUXER_EMAIL_PROVIDER = "none" +FLUXER_EMAIL_FROM_EMAIL = "noreply@example.com" +FLUXER_EMAIL_FROM_NAME = "Fluxer" +FLUXER_EMAIL_SMTP_HOST = "" +FLUXER_EMAIL_SMTP_PORT = "587" +FLUXER_EMAIL_SMTP_USERNAME = "" +FLUXER_EMAIL_SMTP_PASSWORD = "" +FLUXER_EMAIL_SMTP_SECURE = "true" + +FLUXER_CAPTCHA_ENABLED = "false" +FLUXER_CAPTCHA_PROVIDER = "none" +FLUXER_DISCOVERY_ENABLED = "true" +FLUXER_KLIPY_API_KEY = "" + +[config] +env = [ + "# HTTPS SETUP INSTRUCTIONS:", + "# By default, this template starts over HTTP for initial testing/setup.", + "# To enable HTTPS (Required for most services to work!)", + "# 1. Change FLUXER_PUBLIC_SCHEME from 'http' to 'https'", + "# 2. Change FLUXER_PUBLIC_PORT from '80' to '443'", + "# 3. Enable HTTPS on your domain in the Dokploy Domains tab and change it if needed.", + "# 4. Change FLUXER_DOMAIN from ${FLUXER_DOMAIN} to the new domain if needed.", + "# 5. Change FLUXER_PASSKEY_RP_ID from ${FLUXER_DOMAIN} to the new domain if needed.", + "# 6. Update FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS from ${FLUXER_DOMAIN} to the new domain if needed.", + "", + "# The FLUXER_DOMAIN and FLUXER_PASSKEY_RP_ID should not have http/https at the start.", + "", + "FLUXER_DOMAIN=${FLUXER_DOMAIN}", + "FLUXER_PUBLIC_SCHEME=${FLUXER_PUBLIC_SCHEME}", + "FLUXER_PUBLIC_PORT=${FLUXER_PUBLIC_PORT}", + "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}", + "MEILI_MASTER_KEY=${MEILI_MASTER_KEY}", + "FLUXER_S3_ACCESS_KEY=${FLUXER_S3_ACCESS_KEY}", + "FLUXER_S3_SECRET_KEY=${FLUXER_S3_SECRET_KEY}", + "FLUXER_SUDO_MODE_SECRET=${FLUXER_SUDO_MODE_SECRET}", + "FLUXER_CONNECTION_INITIATION_SECRET=${FLUXER_CONNECTION_INITIATION_SECRET}", + "FLUXER_GATEWAY_RPC_AUTH_TOKEN=${FLUXER_GATEWAY_RPC_AUTH_TOKEN}", + "FLUXER_MEDIA_PROXY_SECRET_KEY=${FLUXER_MEDIA_PROXY_SECRET_KEY}", + "FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64=${FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64}", + "FLUXER_ADMIN_SECRET_KEY_BASE=${FLUXER_ADMIN_SECRET_KEY_BASE}", + "FLUXER_ADMIN_OAUTH_CLIENT_SECRET=${FLUXER_ADMIN_OAUTH_CLIENT_SECRET}", + "", + "FLUXER_VAPID_PUBLIC_KEY=${FLUXER_VAPID_PUBLIC_KEY}", + "FLUXER_VAPID_PRIVATE_KEY=${FLUXER_VAPID_PRIVATE_KEY}", + "FLUXER_VAPID_EMAIL=${FLUXER_VAPID_EMAIL}", + "", + "LIVEKIT_API_KEY=${LIVEKIT_API_KEY}", + "LIVEKIT_API_SECRET=${LIVEKIT_API_SECRET}", + "", + "FLUXER_PASSKEY_RP_NAME=${FLUXER_PASSKEY_RP_NAME}", + "FLUXER_PASSKEY_RP_ID=${FLUXER_PASSKEY_RP_ID}", + "FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS=${FLUXER_PASSKEY_ADDITIONAL_ALLOWED_ORIGINS}", + "", + "FLUXER_EMAIL_ENABLED=${FLUXER_EMAIL_ENABLED}", + "FLUXER_EMAIL_PROVIDER=${FLUXER_EMAIL_PROVIDER}", + "FLUXER_EMAIL_FROM_EMAIL=${FLUXER_EMAIL_FROM_EMAIL}", + "FLUXER_EMAIL_FROM_NAME=${FLUXER_EMAIL_FROM_NAME}", + "FLUXER_EMAIL_SMTP_HOST=${FLUXER_EMAIL_SMTP_HOST}", + "FLUXER_EMAIL_SMTP_PORT=${FLUXER_EMAIL_SMTP_PORT}", + "FLUXER_EMAIL_SMTP_USERNAME=${FLUXER_EMAIL_SMTP_USERNAME}", + "FLUXER_EMAIL_SMTP_PASSWORD=${FLUXER_EMAIL_SMTP_PASSWORD}", + "FLUXER_EMAIL_SMTP_SECURE=${FLUXER_EMAIL_SMTP_SECURE}", + "", + "FLUXER_CAPTCHA_ENABLED=${FLUXER_CAPTCHA_ENABLED}", + "FLUXER_CAPTCHA_PROVIDER=${FLUXER_CAPTCHA_PROVIDER}", + "FLUXER_DISCOVERY_ENABLED=${FLUXER_DISCOVERY_ENABLED}", + "", + "FLUXER_KLIPY_API_KEY=${FLUXER_KLIPY_API_KEY}", +] +mounts = [] + +[[config.domains]] +serviceName = "caddy" +port = 80 +host = "${FLUXER_DOMAIN}" \ No newline at end of file diff --git a/blueprints/fmd-server/docker-compose.yml b/blueprints/fmd-server/docker-compose.yml index 85bf58767..22a3a199e 100644 --- a/blueprints/fmd-server/docker-compose.yml +++ b/blueprints/fmd-server/docker-compose.yml @@ -1,7 +1,7 @@ services: fmd-server: - image: registry.gitlab.com/fmd-foss/fmd-server:v0.11.0 - ports: + image: registry.gitlab.com/fmd-foss/fmd-server:0.16.0 + expose: - 8080 environment: # View all configurations at: diff --git a/blueprints/fmd-server/meta.json b/blueprints/fmd-server/meta.json index f222c94ae..e2ac9931f 100644 --- a/blueprints/fmd-server/meta.json +++ b/blueprints/fmd-server/meta.json @@ -1,7 +1,7 @@ { "id": "fmd-server", "name": "FMD Server", - "version": "0.11.0", + "version": "0.16.0", "description": "A server to communicate with the FMD Android app, to locate and control your devices.", "logo": "fmd-server.svg", "links": { diff --git a/blueprints/focalboard/docker-compose.yml b/blueprints/focalboard/docker-compose.yml index b2283c7ae..b33b687cb 100644 --- a/blueprints/focalboard/docker-compose.yml +++ b/blueprints/focalboard/docker-compose.yml @@ -25,4 +25,4 @@ volumes: focalboardData: driver: local focalboardPostgre: - driver: local \ No newline at end of file + driver: local diff --git a/blueprints/fonoster/docker-compose.yml b/blueprints/fonoster/docker-compose.yml index 38ddb5718..7ea86c529 100644 --- a/blueprints/fonoster/docker-compose.yml +++ b/blueprints/fonoster/docker-compose.yml @@ -1,8 +1,9 @@ +# dokploy: allow-host-ports — VoIP stack: SIP signaling (5060-5063 TCP/UDP), RTP media (10000-10100/udp) and the gRPC API endpoints (envoy 8449, rtpengine control 8080) are reached directly by phones/SDKs; these protocols cannot be routed by Traefik. services: dashboard: image: fonoster/dashboard:0.15.15 restart: unless-stopped - ports: + expose: - 3030 environment: - SERVER_DASHBOARD_SESSION_SECRET=${SERVER_DASHBOARD_SESSION_SECRET} @@ -199,7 +200,6 @@ services: - ../files/config:/etc/envoy:ro ports: - 8449:8449 - volumes: db: influxdb: diff --git a/blueprints/forgejo/docker-compose.yml b/blueprints/forgejo/docker-compose.yml index 36733c6ea..8090b3863 100644 --- a/blueprints/forgejo/docker-compose.yml +++ b/blueprints/forgejo/docker-compose.yml @@ -1,7 +1,8 @@ -version: "3.8" services: forgejo: - image: codeberg.org/forgejo/forgejo:10 + image: codeberg.org/forgejo/forgejo:16 + depends_on: + - db environment: - USER_UID=${USER_UID} - USER_GID=${USER_GID} @@ -10,15 +11,17 @@ services: - FORGEJO__database__NAME=forgejo - FORGEJO__database__USER=forgejo - FORGEJO__database__PASSWD=forgejo + - FORGEJO__server__DOMAIN=${FORGEJO_DOMAIN} + - FORGEJO__server__SSH_DOMAIN=${FORGEJO_SSH_DOMAIN} + - FORGEJO__server__ROOT_URL=https://${FORGEJO_DOMAIN}/ restart: always - + expose: + - 3000 + - 22 volumes: - forgejo_server:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - depends_on: - - db - db: image: postgres:17 restart: always @@ -26,12 +29,10 @@ services: - POSTGRES_USER=forgejo - POSTGRES_PASSWORD=forgejo - POSTGRES_DB=forgejo - volumes: - forgejo_db:/var/lib/postgresql/data - volumes: forgejo_db: driver: local forgejo_server: - driver: local \ No newline at end of file + driver: local diff --git a/blueprints/forgejo/meta.json b/blueprints/forgejo/meta.json index f7de1ab4a..fce7b4390 100644 --- a/blueprints/forgejo/meta.json +++ b/blueprints/forgejo/meta.json @@ -1,7 +1,7 @@ { "id": "forgejo", "name": "Forgejo", - "version": "10", + "version": "16", "description": "Forgejo is a self-hosted lightweight software forge. Easy to install and low maintenance, it just does the job", "logo": "forgejo.svg", "links": { diff --git a/blueprints/forgejo/template.toml b/blueprints/forgejo/template.toml index 56f45c181..42c357243 100644 --- a/blueprints/forgejo/template.toml +++ b/blueprints/forgejo/template.toml @@ -2,7 +2,12 @@ main_domain = "${domain}" [config] -env = ["USER_UID=1000", "USER_GID=1000"] +env = [ + "USER_UID=1000", + "USER_GID=1000", + "FORGEJO_DOMAIN=${main_domain}", + "FORGEJO_SSH_DOMAIN=${main_domain}" +] mounts = [] [[config.domains]] diff --git a/blueprints/formbricks/docker-compose.yml b/blueprints/formbricks/docker-compose.yml index 55300d4e8..9d1107793 100644 --- a/blueprints/formbricks/docker-compose.yml +++ b/blueprints/formbricks/docker-compose.yml @@ -25,7 +25,7 @@ services: image: ghcr.io/formbricks/formbricks:v3.1.5 depends_on: - postgres - ports: + expose: - 3000 volumes: - ../files/uploads:/home/nextjs/apps/web/uploads/ diff --git a/blueprints/frappe-hr/docker-compose.yml b/blueprints/frappe-hr/docker-compose.yml index a7ce9b262..20d86585d 100644 --- a/blueprints/frappe-hr/docker-compose.yml +++ b/blueprints/frappe-hr/docker-compose.yml @@ -10,8 +10,6 @@ services: <<: *custom_image volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -42,8 +40,6 @@ services: volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: @@ -63,8 +59,6 @@ services: - default volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -87,8 +81,6 @@ services: - long volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -111,8 +103,6 @@ services: - short volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network healthcheck: test: - CMD @@ -145,8 +135,6 @@ services: required: true volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network websocket: <<: *custom_image @@ -167,8 +155,6 @@ services: required: true volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network configurator: <<: *custom_image @@ -197,8 +183,6 @@ services: REGENERATE_APPS_TXT: "${REGENERATE_APPS_TXT:-0}" volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network create-site: <<: *custom_image @@ -237,8 +221,6 @@ services: DB_PORT: "${DB_PORT:-3306}" DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} INSTALL_APP_ARGS: ${INSTALL_APP_ARGS} - networks: - - bench-network migration: <<: *custom_image @@ -258,8 +240,6 @@ services: bench --site all set-config -p pause_scheduler 0; volumes: - sites:/home/frappe/frappe-bench/sites - networks: - - bench-network db: image: mariadb:10.6 @@ -282,8 +262,6 @@ services: - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} volumes: - db-data:/var/lib/mysql - networks: - - bench-network redis-cache: deploy: @@ -292,8 +270,6 @@ services: image: redis:6.2-alpine volumes: - redis-cache-data:/data - networks: - - bench-network healthcheck: test: - CMD @@ -310,8 +286,6 @@ services: image: redis:6.2-alpine volumes: - redis-queue-data:/data - networks: - - bench-network healthcheck: test: - CMD @@ -328,8 +302,6 @@ services: image: redis:6.2-alpine volumes: - redis-socketio-data:/data - networks: - - bench-network healthcheck: test: - CMD @@ -349,6 +321,3 @@ volumes: type: "${SITE_VOLUME_TYPE}" o: "${SITE_VOLUME_OPTS}" device: "${SITE_VOLUME_DEV}" - -networks: - bench-network: \ No newline at end of file diff --git a/blueprints/freshrss/docker-compose.yml b/blueprints/freshrss/docker-compose.yml index 8965edc42..248b82f60 100644 --- a/blueprints/freshrss/docker-compose.yml +++ b/blueprints/freshrss/docker-compose.yml @@ -7,7 +7,7 @@ services: - freshrss_data:/var/www/FreshRSS/data # Optional volume for storing third-party extensions - freshrss_extensions:/var/www/FreshRSS/extensions - ports: + expose: - "80" environment: # Server timezone diff --git a/blueprints/garage-with-ui/docker-compose.yml b/blueprints/garage-with-ui/docker-compose.yml index 7495e1680..8b2800fc0 100644 --- a/blueprints/garage-with-ui/docker-compose.yml +++ b/blueprints/garage-with-ui/docker-compose.yml @@ -6,18 +6,17 @@ services: - garage-storage:/var/lib/garage - garage-storage:/var/lib/garage restart: unless-stopped - ports: + expose: - 3900 - 3901 - 3902 - 3903 - garage-webui: image: khairul169/garage-webui:1.1.0 restart: unless-stopped volumes: - ../files/garage.toml:/etc/garage.toml:ro - ports: + expose: - 3909 environment: - AUTH_USER_PASS @@ -25,4 +24,4 @@ services: - S3_ENDPOINT_URL volumes: - garage-storage: {} \ No newline at end of file + garage-storage: {} diff --git a/blueprints/garage/docker-compose.yml b/blueprints/garage/docker-compose.yml index 105049344..b6b9253f5 100644 --- a/blueprints/garage/docker-compose.yml +++ b/blueprints/garage/docker-compose.yml @@ -6,11 +6,10 @@ services: - garage-storage:/var/lib/garage - garage-storage:/var/lib/garage restart: unless-stopped - ports: + expose: - 3900 - 3901 - 3902 - 3903 - volumes: - garage-storage: {} \ No newline at end of file + garage-storage: {} diff --git a/blueprints/gel/docker-compose.yml b/blueprints/gel/docker-compose.yml index 248909117..65215fe4d 100644 --- a/blueprints/gel/docker-compose.yml +++ b/blueprints/gel/docker-compose.yml @@ -3,7 +3,7 @@ services: gel: image: geldata/gel:6 restart: unless-stopped - ports: + expose: - 5656 environment: - GEL_SERVER_SECURITY=${GEL_SERVER_SECURITY} diff --git a/blueprints/gitea-mysql/template.toml b/blueprints/gitea-mysql/template.toml index 7f087168a..99c4ae502 100644 --- a/blueprints/gitea-mysql/template.toml +++ b/blueprints/gitea-mysql/template.toml @@ -4,6 +4,7 @@ db_password = "${password:24}" db_root_password = "${password:24}" [config] +mounts = [] [[config.domains]] serviceName = "gitea" port = 3000 @@ -19,5 +20,3 @@ GITEA__database__USER = "gitea" GITEA__database__PASSWD = "${db_password}" GITEA_DB_PASSWORD = "${db_password}" MYSQL_ROOT_PASSWORD = "${db_root_password}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/gitea-postgres/template.toml b/blueprints/gitea-postgres/template.toml index f5b152389..0f5ed029b 100644 --- a/blueprints/gitea-postgres/template.toml +++ b/blueprints/gitea-postgres/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" db_password = "${password:24}" [config] +mounts = [] [[config.domains]] serviceName = "gitea" port = 3000 @@ -17,5 +18,3 @@ GITEA__database__NAME = "gitea" GITEA__database__USER = "gitea" GITEA__database__PASSWD = "${db_password}" GITEA_DB_PASSWORD = "${db_password}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/gitea-sqlite/template.toml b/blueprints/gitea-sqlite/template.toml index 49368eec8..214a83a40 100644 --- a/blueprints/gitea-sqlite/template.toml +++ b/blueprints/gitea-sqlite/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "gitea" port = 3000 @@ -19,5 +20,3 @@ USER_GID = "1000" # GITEA__mailer__SMTP_PORT = "465" # GITEA__mailer__USER = "apikey" # GITEA__mailer__PASSWD = "${password:32}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/glance/docker-compose.yml b/blueprints/glance/docker-compose.yml index ace8bc940..43510fab1 100644 --- a/blueprints/glance/docker-compose.yml +++ b/blueprints/glance/docker-compose.yml @@ -6,6 +6,6 @@ services: - ../files/app/assets:/app/assets # Optionally, also mount docker socket if you want to use the docker containers widget # - /var/run/docker.sock:/var/run/docker.sock:ro - ports: + expose: - 8080 - env_file: .env \ No newline at end of file + env_file: .env diff --git a/blueprints/go-whatsapp-web-multidevice/docker-compose.yml b/blueprints/go-whatsapp-web-multidevice/docker-compose.yml index 3f998bb66..3d57f662c 100644 --- a/blueprints/go-whatsapp-web-multidevice/docker-compose.yml +++ b/blueprints/go-whatsapp-web-multidevice/docker-compose.yml @@ -2,7 +2,7 @@ services: whatsapp: image: aldinokemal2104/go-whatsapp-web-multidevice restart: always - ports: + expose: - "3080" volumes: - whatsapp:/app/storages diff --git a/blueprints/gotify/docker-compose.yml b/blueprints/gotify/docker-compose.yml index 1d3d82656..c23a65f95 100644 --- a/blueprints/gotify/docker-compose.yml +++ b/blueprints/gotify/docker-compose.yml @@ -4,8 +4,7 @@ services: restart: unless-stopped volumes: - gotify_data:/app/data - ports: + expose: - "80" - volumes: gotify_data: diff --git a/blueprints/grimoire/docker-compose.yml b/blueprints/grimoire/docker-compose.yml index d497f9826..d0bdd66f4 100644 --- a/blueprints/grimoire/docker-compose.yml +++ b/blueprints/grimoire/docker-compose.yml @@ -4,7 +4,7 @@ services: restart: unless-stopped volumes: - grimoire_data:/app/data - ports: + expose: - "5173" volumes: grimoire_data: {} diff --git a/blueprints/grimoire/template.toml b/blueprints/grimoire/template.toml index 3f624fced..272d6c28e 100644 --- a/blueprints/grimoire/template.toml +++ b/blueprints/grimoire/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "grimoire" port = 5173 @@ -12,5 +13,3 @@ PORT = "5173" PUBLIC_ORIGIN = "https://${main_domain}" PUBLIC_HTTPS_ONLY = "true" PUBLIC_SIGNUP_DISABLED = "false" - -[[config.mounts]] diff --git a/blueprints/grist/docker-compose.yml b/blueprints/grist/docker-compose.yml index 4301c7173..8a6cd93d4 100644 --- a/blueprints/grist/docker-compose.yml +++ b/blueprints/grist/docker-compose.yml @@ -3,7 +3,7 @@ services: grist: image: gristlabs/grist:latest restart: unless-stopped - ports: + expose: - 8484 volumes: - grist_data:/persist diff --git a/blueprints/grist/template.toml b/blueprints/grist/template.toml index 9792749ff..5bc5bfab5 100644 --- a/blueprints/grist/template.toml +++ b/blueprints/grist/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" default_email = "${email}" [config] +mounts = [] [[config.domains]] serviceName = "grist" port = 8484 @@ -10,6 +11,3 @@ host = "${main_domain}" [config.env] GRIST_DEFAULT_EMAIL = "${default_email}" - -[[config.mounts]] - diff --git a/blueprints/habitica/docker-compose.yml b/blueprints/habitica/docker-compose.yml index 3990e571e..c72618e38 100644 --- a/blueprints/habitica/docker-compose.yml +++ b/blueprints/habitica/docker-compose.yml @@ -19,9 +19,8 @@ services: restart: unless-stopped depends_on: - server - ports: + expose: - "80" - mongo: image: docker.io/mongo:latest restart: unless-stopped diff --git a/blueprints/habitica/template.toml b/blueprints/habitica/template.toml index a8cb5fc69..51ac47fd3 100644 --- a/blueprints/habitica/template.toml +++ b/blueprints/habitica/template.toml @@ -6,6 +6,7 @@ mongo_admin_password = "${password}" mongo_habitica_password = "${password}" [config] +mounts = [] [[config.domains]] serviceName = "client" port = 80 @@ -24,9 +25,3 @@ MONGO_ADMIN_PASSWORD = "${mongo_admin_password}" MONGO_HABITICA_USER = "habitica" MONGO_HABITICA_PASSWORD = "${mongo_habitica_password}" ADMIN_EMAIL = "no-reply@${main_domain}" - -[[config.mounts]] -serviceName = "mongo" -type = "volume" -source = "habitica-mongo-data" -target = "/data/db" diff --git a/blueprints/hermes/docker-compose.yml b/blueprints/hermes/docker-compose.yml index 6e915e33c..623a4d359 100644 --- a/blueprints/hermes/docker-compose.yml +++ b/blueprints/hermes/docker-compose.yml @@ -4,7 +4,7 @@ services: image: nousresearch/hermes-agent:v2026.6.19 restart: unless-stopped command: gateway run - ports: + expose: - 9119 - 8642 environment: diff --git a/blueprints/heyform/docker-compose.yml b/blueprints/heyform/docker-compose.yml index 3374befc2..1dec7d438 100644 --- a/blueprints/heyform/docker-compose.yml +++ b/blueprints/heyform/docker-compose.yml @@ -8,7 +8,7 @@ services: depends_on: - mongo - keydb - ports: + expose: - 8000 env_file: - .env diff --git a/blueprints/hi-events/docker-compose.yml b/blueprints/hi-events/docker-compose.yml index dd3d7269a..e83a87efc 100644 --- a/blueprints/hi-events/docker-compose.yml +++ b/blueprints/hi-events/docker-compose.yml @@ -39,4 +39,4 @@ services: - pg_hi-events_data:/var/lib/postgresql/data volumes: - pg_hi-events_data: \ No newline at end of file + pg_hi-events_data: diff --git a/blueprints/hoarder/docker-compose.yml b/blueprints/hoarder/docker-compose.yml index 74a3f1d99..87526b29b 100644 --- a/blueprints/hoarder/docker-compose.yml +++ b/blueprints/hoarder/docker-compose.yml @@ -4,7 +4,7 @@ services: restart: unless-stopped volumes: - hoarder-data:/data - ports: + expose: - 3000 environment: - DISABLE_SIGNUPS @@ -43,4 +43,4 @@ services: retries: 15 volumes: meilisearch-data: - hoarder-data: \ No newline at end of file + hoarder-data: diff --git a/blueprints/homarr/docker-compose.yml b/blueprints/homarr/docker-compose.yml index c43e62f3c..8e3ff3e5c 100644 --- a/blueprints/homarr/docker-compose.yml +++ b/blueprints/homarr/docker-compose.yml @@ -7,8 +7,7 @@ services: - homarr_appdata:/appdata environment: - SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY} - ports: + expose: - 7575 - volumes: homarr_appdata: diff --git a/blueprints/homeassistant/docker-compose.yml b/blueprints/homeassistant/docker-compose.yml index 61d2dee6d..4a5ece2e8 100644 --- a/blueprints/homeassistant/docker-compose.yml +++ b/blueprints/homeassistant/docker-compose.yml @@ -2,7 +2,7 @@ services: homeassistant: image: ghcr.io/home-assistant/home-assistant:stable restart: unless-stopped - ports: + expose: - 8123 volumes: - ../files/configuration.yaml:/config/configuration.yaml diff --git a/blueprints/homebridge/docker-compose.yml b/blueprints/homebridge/docker-compose.yml index d8aba2a9b..1584821e1 100644 --- a/blueprints/homebridge/docker-compose.yml +++ b/blueprints/homebridge/docker-compose.yml @@ -2,7 +2,7 @@ services: homebridge: image: homebridge/homebridge:latest restart: always - ports: + expose: - 8581 volumes: - ./volumes/homebridge:/homebridge diff --git a/blueprints/hoppscotch/template.toml b/blueprints/hoppscotch/template.toml index c295f8149..446e9b550 100644 --- a/blueprints/hoppscotch/template.toml +++ b/blueprints/hoppscotch/template.toml @@ -4,7 +4,7 @@ db_password = "${password:32}" encryption_key = "${password:32}" [config] - +mounts = [] [[config.domains]] serviceName = "hoppscotch" port = 80 @@ -37,5 +37,3 @@ VITE_APP_PRIVACY_POLICY_LINK = "https://docs.hoppscotch.io/support/privacy" # Subpath Access ENABLE_SUBPATH_BASED_ACCESS = "true" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/hortusfox/template.toml b/blueprints/hortusfox/template.toml index 19706ecd7..80a4fb41f 100644 --- a/blueprints/hortusfox/template.toml +++ b/blueprints/hortusfox/template.toml @@ -10,7 +10,7 @@ DB_PASSWORD = "${password:20}" MARIADB_ROOT_PASSWORD = "${password:24}" [config] - +mounts = [] [[config.domains]] serviceName = "app" port = 80 @@ -24,27 +24,3 @@ DB_DATABASE = "${DB_DATABASE}" DB_USERNAME = "${DB_USERNAME}" DB_PASSWORD = "${DB_PASSWORD}" MARIADB_ROOT_PASSWORD = "${MARIADB_ROOT_PASSWORD}" - -[[config.mounts]] -name = "app_images" -mountPath = "/var/www/html/public/img" - -[[config.mounts]] -name = "app_logs" -mountPath = "/var/www/html/app/logs" - -[[config.mounts]] -name = "app_backup" -mountPath = "/var/www/html/public/backup" - -[[config.mounts]] -name = "app_themes" -mountPath = "/var/www/html/public/themes" - -[[config.mounts]] -name = "app_migrate" -mountPath = "/var/www/html/app/migrations" - -[[config.mounts]] -name = "db_data" -mountPath = "/var/lib/mysql" diff --git a/blueprints/huly/docker-compose.yml b/blueprints/huly/docker-compose.yml index 639b10d2f..f0aea9a6e 100644 --- a/blueprints/huly/docker-compose.yml +++ b/blueprints/huly/docker-compose.yml @@ -4,7 +4,7 @@ services: nginx: image: "nginx:1.21.3" - ports: + expose: - 80 volumes: - ../files/volumes/nginx/.huly.nginx:/etc/nginx/conf.d/default.conf diff --git a/blueprints/i18n-blog/template.toml b/blueprints/i18n-blog/template.toml index 2af9bb7b6..ea2b16e22 100644 --- a/blueprints/i18n-blog/template.toml +++ b/blueprints/i18n-blog/template.toml @@ -4,7 +4,7 @@ jwt_secret = "${password:32}" # secure default recovery_mode = "false" [config] - +mounts = [] [[config.domains]] serviceName = "kuno" port = 80 @@ -18,7 +18,3 @@ GIN_MODE = "release" NODE_ENV = "production" JWT_SECRET = "${jwt_secret}" RECOVERY_MODE = "${recovery_mode}" - -[[config.mounts]] -name = "kuno-data" -mountPath = "/app/data" diff --git a/blueprints/ihatemoney/template.toml b/blueprints/ihatemoney/template.toml index 94d3e6e75..0ebfd131c 100644 --- a/blueprints/ihatemoney/template.toml +++ b/blueprints/ihatemoney/template.toml @@ -6,6 +6,7 @@ MAIL_PASSWORD = "${password:32}" SECRET_KEY = "${password:64}" [config] +mounts = [] [[config.domains]] serviceName = "ihatemoney" port = 8000 @@ -37,7 +38,3 @@ LEGAL_LINK = "" PORT = "8000" PUID = "0" PGID = "0" - -[[config.mounts]] -volumeName = "sqlite-db" -mountPath = "/database" diff --git a/blueprints/influxdb/docker-compose.yml b/blueprints/influxdb/docker-compose.yml index 1327c6028..0e724be76 100644 --- a/blueprints/influxdb/docker-compose.yml +++ b/blueprints/influxdb/docker-compose.yml @@ -8,4 +8,4 @@ services: volumes: influxdb2-data: - influxdb2-config: \ No newline at end of file + influxdb2-config: diff --git a/blueprints/inngest/docker-compose.yml b/blueprints/inngest/docker-compose.yml index 601184f16..a915ff56c 100644 --- a/blueprints/inngest/docker-compose.yml +++ b/blueprints/inngest/docker-compose.yml @@ -35,7 +35,7 @@ services: - INNGEST_LOG_LEVEL=${INNGEST_LOG_LEVEL:-info} - INNGEST_JSON=${INNGEST_JSON:-false} - INNGEST_VERBOSE=${INNGEST_VERBOSE:-false} - ports: + expose: - 8288 depends_on: postgres: @@ -62,7 +62,7 @@ services: - PGUSER=${POSTGRES_USER} volumes: - postgres_data:/var/lib/postgresql/data - ports: + expose: - 5432 healthcheck: test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'] @@ -78,7 +78,7 @@ services: - net.core.somaxconn=1024 volumes: - redis_data:/data - ports: + expose: - 6379 healthcheck: test: ['CMD', 'redis-cli', 'ping'] diff --git a/blueprints/instantdb/docker-compose.yml b/blueprints/instantdb/docker-compose.yml index b40e043dd..e5d3b8d18 100644 --- a/blueprints/instantdb/docker-compose.yml +++ b/blueprints/instantdb/docker-compose.yml @@ -45,7 +45,7 @@ services: PYTHONUNBUFFERED: "1" NODE_ENV: "production" command: ["java", "-Djava.awt.headless=true", "-server", "-jar", "target/instant-standalone.jar"] - ports: + expose: - "8888" - "6005" logging: diff --git a/blueprints/ipfs/docker-compose.yml b/blueprints/ipfs/docker-compose.yml index 4275f5535..e04e3531c 100644 --- a/blueprints/ipfs/docker-compose.yml +++ b/blueprints/ipfs/docker-compose.yml @@ -8,7 +8,7 @@ services: volumes: - ipfs_data:/data/ipfs - ipfs_staging:/export - ports: + expose: - 4001 - 8080 - 5001 diff --git a/blueprints/java/docker-compose.yml b/blueprints/java/docker-compose.yml index 7d3d02cdf..fa87f637a 100644 --- a/blueprints/java/docker-compose.yml +++ b/blueprints/java/docker-compose.yml @@ -11,9 +11,9 @@ services: - STARTUP=${STARTUP_COMMAND} - SERVER_JARFILE=${SERVER_JARFILE} - JAVA_VERSION=${JAVA_VERSION} - ports: + expose: - ${SERVER_PORT} user: container volumes: - app-data: {} \ No newline at end of file + app-data: {} diff --git a/blueprints/jellyseerr/docker-compose.yml b/blueprints/jellyseerr/docker-compose.yml index e17f3ddb1..13bbd473a 100644 --- a/blueprints/jellyseerr/docker-compose.yml +++ b/blueprints/jellyseerr/docker-compose.yml @@ -6,8 +6,7 @@ services: - jellyseerr_config:/app/config environment: LOG_LEVEL: info - ports: + expose: - "5055" - volumes: jellyseerr_config: diff --git a/blueprints/jenkins/docker-compose.yml b/blueprints/jenkins/docker-compose.yml index f2b8ad3cb..0c9971fcf 100644 --- a/blueprints/jenkins/docker-compose.yml +++ b/blueprints/jenkins/docker-compose.yml @@ -6,8 +6,7 @@ services: volumes: - jenkins-home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock - ports: + expose: - 8080 - volumes: - jenkins-home: {} \ No newline at end of file + jenkins-home: {} diff --git a/blueprints/jitsi/docker-compose.yml b/blueprints/jitsi/docker-compose.yml index 3e235416e..011c5f857 100644 --- a/blueprints/jitsi/docker-compose.yml +++ b/blueprints/jitsi/docker-compose.yml @@ -74,7 +74,7 @@ services: restart: unless-stopped depends_on: - prosody - ports: + expose: - 10000 volumes: - jitsiJvbConfig:/config diff --git a/blueprints/joplin-server/docker-compose.yml b/blueprints/joplin-server/docker-compose.yml index fefac15b1..ea281de79 100644 --- a/blueprints/joplin-server/docker-compose.yml +++ b/blueprints/joplin-server/docker-compose.yml @@ -23,8 +23,7 @@ services: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PORT: 5432 POSTGRES_HOST: joplin-db - ports: + expose: - "22300" - volumes: joplin_db_data: diff --git a/blueprints/kaneo/docker-compose.yml b/blueprints/kaneo/docker-compose.yml index 71e60b167..64061b293 100644 --- a/blueprints/kaneo/docker-compose.yml +++ b/blueprints/kaneo/docker-compose.yml @@ -14,7 +14,7 @@ services: environment: JWT_ACCESS: ${KANEO_JWT_ACCESS} DATABASE_URL: "postgresql://${KANEO_DB_USER}:${KANEO_DB_PASSWORD}@postgres:5432/${KANEO_DB}" - ports: + expose: - 1337 depends_on: postgres: @@ -25,7 +25,7 @@ services: image: ghcr.io/usekaneo/web:latest environment: KANEO_API_URL: "http://${BACKEND_HOST}" - ports: + expose: - 5173 depends_on: backend: diff --git a/blueprints/karakeep/template.toml b/blueprints/karakeep/template.toml index 285f6a1b9..539a79163 100644 --- a/blueprints/karakeep/template.toml +++ b/blueprints/karakeep/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" karakeep_version = "release" [config] +mounts = [] [[config.domains]] serviceName = "web" port = 3000 @@ -106,13 +107,3 @@ host = "${main_domain}" # "CRAWLER_HTTP_PROXY" = "" # "CRAWLER_HTTPS_PROXY" = "" # "CRAWLER_NO_PROXY" = "" - -[[config.mounts]] -# Persistent data directory for Karakeep -volumeName = "data" -mountPath = "/data" - -[[config.mounts]] -# Meilisearch data directory -volumeName = "meilisearch" -mountPath = "/meili_data" diff --git a/blueprints/kener/template.toml b/blueprints/kener/template.toml index bfe09e59f..9059bb210 100644 --- a/blueprints/kener/template.toml +++ b/blueprints/kener/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" KENER_SECRET_KEY = "${password:64}" [config] +mounts = [] [[config.domains]] serviceName = "kener" port = 3000 @@ -16,18 +17,3 @@ REDIS_URL = "redis://redis:6379" DATABASE_URL = "sqlite://./database/kener.sqlite.db" RESEND_API_KEY = "" RESEND_SENDER_EMAIL = "" - -[[config.mounts]] -type = "volume" -source = "data" -target = "/app/database" - -[[config.mounts]] -type = "volume" -source = "redis_data" -target = "/data" - -[[config.mounts]] -type = "bind" -source = "../files/uploads" -target = "/app/uploads" diff --git a/blueprints/kestra/docker-compose.yml b/blueprints/kestra/docker-compose.yml index 8f9672cdf..dcceebd1c 100644 --- a/blueprints/kestra/docker-compose.yml +++ b/blueprints/kestra/docker-compose.yml @@ -57,7 +57,7 @@ services: tmpDir: path: /tmp/kestra-wd/tmp url: http://localhost:8080/ - ports: + expose: - "8080" - "8081" depends_on: diff --git a/blueprints/kestra/template.toml b/blueprints/kestra/template.toml index c941e4260..fbc39bc7a 100644 --- a/blueprints/kestra/template.toml +++ b/blueprints/kestra/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "kestra" port = 8080 @@ -9,5 +10,3 @@ host = "${main_domain}" [config.env] - -[[config.mounts]] diff --git a/blueprints/keycloak/template.toml b/blueprints/keycloak/template.toml index 86c7107a4..f2f29b5a3 100644 --- a/blueprints/keycloak/template.toml +++ b/blueprints/keycloak/template.toml @@ -8,6 +8,7 @@ KEYCLOAK_ADMIN_PASSWORD = "${password:32}" KC_HOSTNAME = "${main_domain}" [config] +mounts = [] [[config.domains]] serviceName = "keycloak" port = 8080 @@ -20,5 +21,3 @@ POSTGRES_PASSWORD = "${POSTGRES_PASSWORD}" KEYCLOAK_ADMIN = "${KEYCLOAK_ADMIN}" KEYCLOAK_ADMIN_PASSWORD = "${KEYCLOAK_ADMIN_PASSWORD}" KC_HOSTNAME = "${KC_HOSTNAME}" - -[[config.mounts]] diff --git a/blueprints/kimai/docker-compose.yml b/blueprints/kimai/docker-compose.yml index c97cadfbc..e73d6147a 100644 --- a/blueprints/kimai/docker-compose.yml +++ b/blueprints/kimai/docker-compose.yml @@ -42,4 +42,4 @@ services: volumes: kimai-data: - mysql-data: \ No newline at end of file + mysql-data: diff --git a/blueprints/kitchenowl/template.toml b/blueprints/kitchenowl/template.toml index 883d9d411..bf8adae48 100644 --- a/blueprints/kitchenowl/template.toml +++ b/blueprints/kitchenowl/template.toml @@ -6,7 +6,7 @@ db_user = "kitchenowl" db_name = "kitchenowl" [config] - +mounts = [] [[config.domains]] serviceName = "web" port = 8080 @@ -20,14 +20,4 @@ DB_NAME = "${db_name}" DB_USER = "${db_user}" DB_PASSWORD = "${db_password}" -# Persist uploads/attachments -[[config.mounts]] -serviceName = "web" -volumeName = "kitchenowl_files" -mountPath = "/data" - -# Persist Postgres data -[[config.mounts]] -serviceName = "db" -volumeName = "kitchenowl_db" -mountPath = "/var/lib/postgresql/data" +# Persist uploads/attachments \ No newline at end of file diff --git a/blueprints/kokoro-tts/docker-compose.yml b/blueprints/kokoro-tts/docker-compose.yml index a0298ce1b..b8fe6b272 100644 --- a/blueprints/kokoro-tts/docker-compose.yml +++ b/blueprints/kokoro-tts/docker-compose.yml @@ -4,7 +4,7 @@ services: context: https://github.com/remsky/Kokoro-FastAPI.git#master dockerfile: docker/cpu/Dockerfile restart: unless-stopped - ports: + expose: - 8880 environment: - MODEL_PATH=/app/models diff --git a/blueprints/kutt/docker-compose.yml b/blueprints/kutt/docker-compose.yml index 947f4d262..226ce8b42 100644 --- a/blueprints/kutt/docker-compose.yml +++ b/blueprints/kutt/docker-compose.yml @@ -21,4 +21,4 @@ services: CONTACT_EMAIL: ${CONTACT_EMAIL} volumes: - kutt_db_data: {} \ No newline at end of file + kutt_db_data: {} diff --git a/blueprints/langflow/docker-compose.yml b/blueprints/langflow/docker-compose.yml index 70495a7a0..078394721 100644 --- a/blueprints/langflow/docker-compose.yml +++ b/blueprints/langflow/docker-compose.yml @@ -4,7 +4,7 @@ services: user: root restart: always pull_policy: always - ports: + expose: - 7860 depends_on: - postgres-langflow diff --git a/blueprints/langfuse/docker-compose.yml b/blueprints/langfuse/docker-compose.yml new file mode 100644 index 000000000..81dcdf1fa --- /dev/null +++ b/blueprints/langfuse/docker-compose.yml @@ -0,0 +1,139 @@ +# Langfuse self-hosted, adapted from the official docker-compose.yml: +# https://github.com/langfuse/langfuse/blob/main/docker-compose.yml +# +# MinIO is exposed on its own domain because Langfuse's web UI generates +# presigned S3 URLs that the browser fetches directly (e.g. media in +# traces) - it cannot resolve the internal "minio" hostname. + +services: + postgres: + image: docker.io/postgres:17 + restart: unless-stopped + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: postgres + TZ: UTC + PGTZ: UTC + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 3s + timeout: 3s + retries: 10 + volumes: + - langfuse-postgres-data:/var/lib/postgresql/data + + clickhouse: + image: docker.io/clickhouse/clickhouse-server:25.12 + restart: unless-stopped + user: "101:101" + environment: + CLICKHOUSE_DB: default + CLICKHOUSE_USER: clickhouse + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} + healthcheck: + test: wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1 + interval: 5s + timeout: 5s + retries: 10 + start_period: 1s + volumes: + - langfuse-clickhouse-data:/var/lib/clickhouse + - langfuse-clickhouse-logs:/var/log/clickhouse-server + + minio: + image: cgr.dev/chainguard/minio + restart: unless-stopped + entrypoint: sh + command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data' + environment: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 1s + timeout: 5s + retries: 5 + start_period: 1s + volumes: + - langfuse-minio-data:/data + + redis: + image: docker.io/redis:7 + restart: unless-stopped + command: > + --requirepass ${REDIS_AUTH} + --maxmemory-policy noeviction + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 3s + timeout: 10s + retries: 10 + volumes: + - langfuse-redis-data:/data + + langfuse-worker: + image: docker.io/langfuse/langfuse-worker:4 + restart: unless-stopped + depends_on: &langfuse-depends-on + postgres: + condition: service_healthy + minio: + condition: service_healthy + redis: + condition: service_healthy + clickhouse: + condition: service_healthy + environment: &langfuse-worker-env + NEXTAUTH_URL: ${NEXTAUTH_URL} + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres + SALT: ${SALT} + ENCRYPTION_KEY: ${ENCRYPTION_KEY} + TELEMETRY_ENABLED: "true" + LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: "false" + CLICKHOUSE_MIGRATION_URL: clickhouse://clickhouse:9000 + CLICKHOUSE_URL: http://clickhouse:8123 + CLICKHOUSE_USER: clickhouse + CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} + CLICKHOUSE_CLUSTER_ENABLED: "false" + LANGFUSE_S3_EVENT_UPLOAD_BUCKET: langfuse + LANGFUSE_S3_EVENT_UPLOAD_REGION: auto + LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: minio + LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD} + LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: http://minio:9000 + LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true" + LANGFUSE_S3_EVENT_UPLOAD_PREFIX: events/ + LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: langfuse + LANGFUSE_S3_MEDIA_UPLOAD_REGION: auto + LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: minio + LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD} + LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: http://minio:9000 + LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: "true" + LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: media/ + LANGFUSE_S3_BATCH_EXPORT_ENABLED: "false" + REDIS_HOST: redis + REDIS_PORT: "6379" + REDIS_AUTH: ${REDIS_AUTH} + REDIS_TLS_ENABLED: "false" + + langfuse-web: + image: docker.io/langfuse/langfuse:4 + restart: unless-stopped + depends_on: *langfuse-depends-on + environment: + <<: *langfuse-worker-env + # Bind Next.js to all interfaces: Docker sets HOSTNAME to the container id, + # which resolves to a single network's IP - with Dokploy's extra proxy + # network attached, Traefik would get connection refused (502) otherwise. + HOSTNAME: "0.0.0.0" + NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} + # Overrides the worker's internal endpoint above: the browser resolves + # presigned media URLs itself, so this one must be publicly reachable. + LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT} + +volumes: + langfuse-postgres-data: + langfuse-clickhouse-data: + langfuse-clickhouse-logs: + langfuse-minio-data: + langfuse-redis-data: diff --git a/blueprints/langfuse/instructions.md b/blueprints/langfuse/instructions.md new file mode 100644 index 000000000..5abefd188 --- /dev/null +++ b/blueprints/langfuse/instructions.md @@ -0,0 +1,26 @@ +# Langfuse + +Self-hosted Langfuse: open-source LLM tracing, prompt management, evaluation and cost/usage analytics, based on the official `docker-compose.yml`. + +## Two domains + +This template exposes two domains: + +- The **main domain** serves the Langfuse web app/API (`langfuse-web`, port 3000). +- The **second domain** exposes MinIO's S3 API (port 9000) directly. Langfuse's web UI generates presigned URLs for media (e.g. images attached to traces) that the *browser* fetches straight from MinIO, so this endpoint has to be publicly reachable — it isn't just an internal implementation detail. + +## First boot + +`langfuse-web` and `langfuse-worker` wait on Postgres, ClickHouse, Redis and MinIO to report healthy, then run their own migrations on startup. Once `langfuse-web` is up, open its domain and create the first user — they become the owner of the initial organization. + +## After enabling HTTPS + +By default `NEXTAUTH_URL` and `LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT` are set to `http://`. If you enable HTTPS on either domain (recommended for production), update the matching environment variable to `https://` and redeploy, otherwise auth callbacks and media URLs will point at the wrong scheme. + +## Licensing + +The core of Langfuse (tracing, prompt management, evaluation, playground) is open-source (MIT) and fully usable self-hosted without a license key. Some enterprise-only features (e.g. SSO/SAML, fine-grained RBAC) live under a separate commercial license — see [self-hosting license docs](https://langfuse.com/self-hosting/license-key). This template does not configure a license key. + +## Versioning + +Images are pinned to the `4` major tag (`langfuse/langfuse:4`, `langfuse/langfuse-worker:4`), matching the upstream compose file. To pin to a specific release instead, replace `4` with a version tag from [Docker Hub](https://hub.docker.com/r/langfuse/langfuse/tags). diff --git a/blueprints/langfuse/langfuse.svg b/blueprints/langfuse/langfuse.svg new file mode 100644 index 000000000..d56d582be --- /dev/null +++ b/blueprints/langfuse/langfuse.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/langfuse/meta.json b/blueprints/langfuse/meta.json new file mode 100644 index 000000000..7b39595eb --- /dev/null +++ b/blueprints/langfuse/meta.json @@ -0,0 +1,13 @@ +{ + "id": "langfuse", + "name": "Langfuse", + "version": "4", + "description": "Langfuse is an open-source LLM engineering platform for tracing, prompt management, evaluation, and cost/usage analytics of LLM applications.", + "logo": "langfuse.svg", + "links": { + "github": "https://github.com/langfuse/langfuse", + "website": "https://langfuse.com/", + "docs": "https://langfuse.com/self-hosting" + }, + "tags": ["ai", "llm", "observability", "analytics", "monitoring"] +} diff --git a/blueprints/langfuse/template.toml b/blueprints/langfuse/template.toml new file mode 100644 index 000000000..9d388a88b --- /dev/null +++ b/blueprints/langfuse/template.toml @@ -0,0 +1,36 @@ +[variables] +main_domain = "${domain}" +minio_domain = "${domain}" +postgres_password = "${password:32}" +clickhouse_password = "${password:32}" +minio_root_password = "${password:32}" +redis_auth = "${password:32}" +nextauth_secret = "${password:32}" +salt = "${password:32}" +encryption_key = "${hash:64}" +nextauth_url = "http://${main_domain}" +minio_public_endpoint = "http://${minio_domain}" + +[config] +[[config.domains]] +serviceName = "langfuse-web" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "minio" +port = 9000 +host = "${minio_domain}" + +[config.env] +POSTGRES_PASSWORD = "${postgres_password}" +CLICKHOUSE_PASSWORD = "${clickhouse_password}" +MINIO_ROOT_PASSWORD = "${minio_root_password}" +REDIS_AUTH = "${redis_auth}" +NEXTAUTH_SECRET = "${nextauth_secret}" +SALT = "${salt}" +ENCRYPTION_KEY = "${encryption_key}" +# Set to https:// once HTTPS is enabled on the domain +NEXTAUTH_URL = "${nextauth_url}" +# Set to https:// once HTTPS is enabled on the domain +LANGFUSE_S3_MEDIA_PUBLIC_ENDPOINT = "${minio_public_endpoint}" diff --git a/blueprints/lavalink/docker-compose.yml b/blueprints/lavalink/docker-compose.yml index 1f4e91cf7..196d1237c 100644 --- a/blueprints/lavalink/docker-compose.yml +++ b/blueprints/lavalink/docker-compose.yml @@ -22,7 +22,7 @@ services: volumes: - "../files/application.yml:/opt/Lavalink/application.yml:rw" - "../files/plugins/:/opt/Lavalink/plugins/:rw" - ports: + expose: - ${SERVER_PORT} healthcheck: diff --git a/blueprints/leantime/docker-compose.yml b/blueprints/leantime/docker-compose.yml index 813748b53..1d9fe9a06 100644 --- a/blueprints/leantime/docker-compose.yml +++ b/blueprints/leantime/docker-compose.yml @@ -3,7 +3,7 @@ services: leantime: image: leantime/leantime:latest restart: unless-stopped - ports: + expose: - 8080 environment: - LEAN_APP_URL=${LEAN_APP_URL} diff --git a/blueprints/letterfeed/template.toml b/blueprints/letterfeed/template.toml index 0e3455485..716124e96 100644 --- a/blueprints/letterfeed/template.toml +++ b/blueprints/letterfeed/template.toml @@ -4,7 +4,7 @@ secret_key = "${password:64}" auth_password = "${password:32}" [config] - +mounts = [] [[config.domains]] serviceName = "frontend" port = 3000 @@ -28,7 +28,3 @@ LETTERFEED_AUTO_ADD_NEW_SENDERS = "false" LETTERFEED_SECRET_KEY = "${secret_key}" LETTERFEED_AUTH_USERNAME = "admin" LETTERFEED_AUTH_PASSWORD = "${auth_password}" - -[[config.mounts]] -name = "letterfeed_data" -mountPath = "/data" \ No newline at end of file diff --git a/blueprints/librechat/docker-compose.yml b/blueprints/librechat/docker-compose.yml index a5a4c5a9e..f27c97ae1 100644 --- a/blueprints/librechat/docker-compose.yml +++ b/blueprints/librechat/docker-compose.yml @@ -107,4 +107,4 @@ volumes: mongo_data: meili_data: postgres_data: - librechat_data: \ No newline at end of file + librechat_data: diff --git a/blueprints/libredb-studio/docker-compose.yml b/blueprints/libredb-studio/docker-compose.yml index 00b887669..ca3f9b9c6 100644 --- a/blueprints/libredb-studio/docker-compose.yml +++ b/blueprints/libredb-studio/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: libredb-studio: - image: ghcr.io/libredb/libredb-studio:0.9.27 + image: ghcr.io/libredb/libredb-studio:0.9.59 restart: unless-stopped environment: - ADMIN_EMAIL=admin@libredb.org diff --git a/blueprints/libredb-studio/meta.json b/blueprints/libredb-studio/meta.json index 32868b858..1bb029c99 100644 --- a/blueprints/libredb-studio/meta.json +++ b/blueprints/libredb-studio/meta.json @@ -1,7 +1,7 @@ { "id": "libredb-studio", "name": "LibreDB Studio", - "version": "0.9.27", + "version": "0.9.59", "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": { diff --git a/blueprints/libredesk/docker-compose.yml b/blueprints/libredesk/docker-compose.yml index 212b528f6..4b1533dd2 100644 --- a/blueprints/libredesk/docker-compose.yml +++ b/blueprints/libredesk/docker-compose.yml @@ -2,7 +2,7 @@ services: libredesk: image: libredesk/libredesk:latest restart: unless-stopped - ports: + expose: - 9000 environment: # If the password is set during first docker-compose up, the system user password will be set to this value. @@ -20,8 +20,7 @@ services: db: image: postgres:17-alpine restart: unless-stopped - ports: - # Only bind on the local interface. To connect to Postgres externally, change this to 0.0.0.0 + expose: - 5432 environment: # Set these environment variables to configure the database, defaults to libredesk. @@ -40,12 +39,11 @@ services: redis: image: redis:7-alpine restart: unless-stopped - ports: - # Only bind on the local interface. + expose: - 6379 volumes: - redis-data:/data volumes: postgres-data: - redis-data: \ No newline at end of file + redis-data: diff --git a/blueprints/libretranslate/docker-compose.yml b/blueprints/libretranslate/docker-compose.yml index f746f550b..bbaf4b14f 100644 --- a/blueprints/libretranslate/docker-compose.yml +++ b/blueprints/libretranslate/docker-compose.yml @@ -4,7 +4,7 @@ services: libretranslate: image: libretranslate/libretranslate:latest restart: unless-stopped - ports: + expose: - "5000" environment: # Enables the API key system diff --git a/blueprints/linkding/docker-compose.yml b/blueprints/linkding/docker-compose.yml index 0d283c368..76f4fe0ae 100644 --- a/blueprints/linkding/docker-compose.yml +++ b/blueprints/linkding/docker-compose.yml @@ -3,7 +3,7 @@ services: linkding: image: sissbruecker/linkding:latest restart: unless-stopped - ports: + expose: - 9090 volumes: - ../files/linkding-data:/etc/linkding/data diff --git a/blueprints/linkstack/template.toml b/blueprints/linkstack/template.toml index e237251d9..7c1d86a59 100644 --- a/blueprints/linkstack/template.toml +++ b/blueprints/linkstack/template.toml @@ -4,6 +4,7 @@ admin_email = "${email}" mysql_root_password = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "linkstack" port = 80 @@ -18,11 +19,3 @@ LOG_LEVEL = "info" PHP_MEMORY_LIMIT = "256M" UPLOAD_MAX_FILESIZE = "8M" MYSQL_ROOT_PASSWORD = "${mysql_root_password}" - -[[config.mounts]] -volume = "linkstack-data" -target = "/htdocs" - -[[config.mounts]] -volume = "mysql-data" -target = "/var/lib/mysql" diff --git a/blueprints/linkwarden/docker-compose.yml b/blueprints/linkwarden/docker-compose.yml index 05ffb8a0a..ba2fd88e3 100644 --- a/blueprints/linkwarden/docker-compose.yml +++ b/blueprints/linkwarden/docker-compose.yml @@ -6,7 +6,7 @@ services: - DATABASE_URL=postgresql://linkwarden:${POSTGRES_PASSWORD}@postgres:5432/linkwarden restart: unless-stopped image: ghcr.io/linkwarden/linkwarden:v2.9.3 - ports: + expose: - 3000 volumes: - linkwarden-data:/data/data diff --git a/blueprints/listmonk/docker-compose.yml b/blueprints/listmonk/docker-compose.yml index a77ebeb96..028223c45 100644 --- a/blueprints/listmonk/docker-compose.yml +++ b/blueprints/listmonk/docker-compose.yml @@ -1,7 +1,7 @@ services: db: image: postgres:17-alpine - ports: + expose: - 5432 environment: - POSTGRES_USER=listmonk diff --git a/blueprints/litellm/template.toml b/blueprints/litellm/template.toml index 1e21dc72c..5bf9b2e4e 100644 --- a/blueprints/litellm/template.toml +++ b/blueprints/litellm/template.toml @@ -29,6 +29,7 @@ novita_api_key = "" infinity_api_key = "" [config] +mounts = [] [[config.domains]] serviceName = "litellm" port = 4000 @@ -63,5 +64,3 @@ ANTHROPIC_API_KEY = "${anthropic_api_key}" INFISICAL_TOKEN = "${infisical_token}" NOVITA_API_KEY = "${novita_api_key}" INFINITY_API_KEY = "${infinity_api_key}" - -[[config.mounts]] diff --git a/blueprints/lobe-chat/docker-compose.yml b/blueprints/lobe-chat/docker-compose.yml index 676140903..6b7ae4965 100644 --- a/blueprints/lobe-chat/docker-compose.yml +++ b/blueprints/lobe-chat/docker-compose.yml @@ -4,9 +4,9 @@ services: lobe-chat: image: lobehub/lobe-chat:v1.26.1 restart: always - ports: + expose: - 3210 environment: OPENAI_API_KEY: sk-xxxx OPENAI_PROXY_URL: https://api-proxy.com/v1 - ACCESS_CODE: lobe66 \ No newline at end of file + ACCESS_CODE: lobe66 diff --git a/blueprints/lodestone/docker-compose.yml b/blueprints/lodestone/docker-compose.yml index 41b444f25..00cfc1133 100644 --- a/blueprints/lodestone/docker-compose.yml +++ b/blueprints/lodestone/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — Lodestone hosts game servers (Minecraft et al.) on 25565-25590 (raw TCP) and the dashboard talks to the core agent on 16662 directly by server address; neither is routable through Traefik. version: '3.8' services: @@ -16,4 +17,4 @@ services: volumes: lodestone: - driver: local \ No newline at end of file + driver: local diff --git a/blueprints/macos/docker-compose.yml b/blueprints/macos/docker-compose.yml index 585c1bf97..43cc88a80 100644 --- a/blueprints/macos/docker-compose.yml +++ b/blueprints/macos/docker-compose.yml @@ -13,4 +13,4 @@ services: stop_grace_period: 2m volumes: - macos-storage: \ No newline at end of file + macos-storage: diff --git a/blueprints/mailpit/docker-compose.yml b/blueprints/mailpit/docker-compose.yml index d0dbdb8ec..ef26770e3 100644 --- a/blueprints/mailpit/docker-compose.yml +++ b/blueprints/mailpit/docker-compose.yml @@ -2,8 +2,8 @@ services: mailpit: image: axllent/mailpit:v1.22.3 restart: unless-stopped - ports: - - '1025:1025' + expose: + - 1025 # SMTP — other services reach it over the internal Docker network (mailpit:1025) volumes: - 'mailpit-data:/data' environment: @@ -22,4 +22,4 @@ services: retries: 10 volumes: - mailpit-data: \ No newline at end of file + mailpit-data: diff --git a/blueprints/mailu/docker-compose.yml b/blueprints/mailu/docker-compose.yml index 3b644361e..baa1423df 100644 --- a/blueprints/mailu/docker-compose.yml +++ b/blueprints/mailu/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — mail protocols (SMTP 25, SMTPS 465, Submission 587, IMAPS 993) are not HTTP; mail servers and clients must reach them directly on the host. version: "3.8" # Shared Mailu configuration (equivalent to the upstream mailu.env file) diff --git a/blueprints/marketing-dashboard/docker-compose.yml b/blueprints/marketing-dashboard/docker-compose.yml index 637849e7f..efb6fcbcd 100644 --- a/blueprints/marketing-dashboard/docker-compose.yml +++ b/blueprints/marketing-dashboard/docker-compose.yml @@ -19,7 +19,7 @@ services: 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: + expose: - 3000 healthcheck: test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/health > /dev/null || exit 1"] diff --git a/blueprints/mautic/docker-compose.yml b/blueprints/mautic/docker-compose.yml index 223ea7ee9..ce2cff8ff 100644 --- a/blueprints/mautic/docker-compose.yml +++ b/blueprints/mautic/docker-compose.yml @@ -30,7 +30,7 @@ services: depends_on: mysql: condition: service_healthy - ports: + expose: - 80 environment: - DOCKER_MAUTIC_ROLE=mautic_web @@ -123,9 +123,8 @@ services: PMA_HOST: mysql PMA_PORT: 3306 UPLOAD_LIMIT: 64M - ports: + expose: - 80 - volumes: mysql_data: mautic_data: diff --git a/blueprints/mcsmanager/docker-compose.yml b/blueprints/mcsmanager/docker-compose.yml index 7994a3827..939cac725 100644 --- a/blueprints/mcsmanager/docker-compose.yml +++ b/blueprints/mcsmanager/docker-compose.yml @@ -2,7 +2,7 @@ services: web: image: githubyumao/mcsmanager-web:latest restart: unless-stopped - ports: + expose: - 23333 volumes: - /etc/localtime:/etc/localtime:ro @@ -11,7 +11,7 @@ services: daemon: image: githubyumao/mcsmanager-daemon:latest restart: unless-stopped - ports: + expose: - 24444 environment: - MCSM_DOCKER_WORKSPACE_PATH=/opt/mcsmanager/daemon/data/InstanceData diff --git a/blueprints/mediacms/docker-compose.yml b/blueprints/mediacms/docker-compose.yml index cf58ba436..172917e1b 100644 --- a/blueprints/mediacms/docker-compose.yml +++ b/blueprints/mediacms/docker-compose.yml @@ -36,7 +36,7 @@ services: image: mediacms/mediacms:latest deploy: replicas: 1 - ports: + expose: - 80 volumes: - mediacms_data:/home/mediacms.io/mediacms/media diff --git a/blueprints/mediafetch/docker-compose.yml b/blueprints/mediafetch/docker-compose.yml index 17ba61761..03dc98e45 100644 --- a/blueprints/mediafetch/docker-compose.yml +++ b/blueprints/mediafetch/docker-compose.yml @@ -18,4 +18,4 @@ services: - VERSION_CHECK_TTL_MS=${VERSION_CHECK_TTL_MS} volumes: - mediafetch_data: \ No newline at end of file + mediafetch_data: diff --git a/blueprints/memos/docker-compose.yml b/blueprints/memos/docker-compose.yml index e66007629..9ead8402c 100644 --- a/blueprints/memos/docker-compose.yml +++ b/blueprints/memos/docker-compose.yml @@ -9,7 +9,7 @@ services: environment: - MEMOS_MODE=${MEMOS_MODE} - MEMOS_PORT=${MEMOS_PORT} - ports: + expose: - "5230" volumes: memos_data: diff --git a/blueprints/memos/template.toml b/blueprints/memos/template.toml index b033bdb7f..eaa59b0ef 100644 --- a/blueprints/memos/template.toml +++ b/blueprints/memos/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "memos" port = 5230 @@ -11,4 +12,3 @@ host = "${main_domain}" [config.env] MEMOS_MODE = "prod" MEMOS_PORT = "5230" -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/metube/docker-compose.yml b/blueprints/metube/docker-compose.yml index 538351b3c..7db6738c1 100644 --- a/blueprints/metube/docker-compose.yml +++ b/blueprints/metube/docker-compose.yml @@ -3,7 +3,7 @@ services: metube: image: ghcr.io/alexta69/metube restart: unless-stopped - ports: + expose: - 8081 volumes: - ../files/downloads:/downloads diff --git a/blueprints/metube/template.toml b/blueprints/metube/template.toml index 9e67b67c8..48037f9fe 100644 --- a/blueprints/metube/template.toml +++ b/blueprints/metube/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "metube" port = 8081 @@ -39,5 +40,3 @@ DOWNLOAD_MODE = "limited" MAX_CONCURRENT_DOWNLOADS = "3" LOGLEVEL = "INFO" ENABLE_ACCESSLOG = "false" - -[[config.mounts]] diff --git a/blueprints/mixpost/docker-compose.yml b/blueprints/mixpost/docker-compose.yml index bf98c7283..cde08e1b8 100644 --- a/blueprints/mixpost/docker-compose.yml +++ b/blueprints/mixpost/docker-compose.yml @@ -45,4 +45,4 @@ volumes: mysql: {} redis: {} storage: {} - logs: {} \ No newline at end of file + logs: {} diff --git a/blueprints/mixpost/template.toml b/blueprints/mixpost/template.toml index 8457113d7..393a19cfe 100644 --- a/blueprints/mixpost/template.toml +++ b/blueprints/mixpost/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" mx_password = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "mixpost" port = 80 @@ -19,5 +20,3 @@ DB_DATABASE="mixpost_db" DB_USERNAME="mixpost_user" DB_PASSWORD="${mx_password}" - -[[config.mounts]] diff --git a/blueprints/moltbot/docker-compose.yml b/blueprints/moltbot/docker-compose.yml index e0014a7a9..ac4c3c301 100644 --- a/blueprints/moltbot/docker-compose.yml +++ b/blueprints/moltbot/docker-compose.yml @@ -13,7 +13,7 @@ services: volumes: - moltbot-config:/home/node/.clawdbot - moltbot-workspace:/home/node/clawd - ports: + expose: - "18789" - "18790" init: true @@ -32,4 +32,4 @@ services: volumes: moltbot-config: - moltbot-workspace: \ No newline at end of file + moltbot-workspace: diff --git a/blueprints/morphos/template.toml b/blueprints/morphos/template.toml index 3e35e2b31..0494ffdd9 100644 --- a/blueprints/morphos/template.toml +++ b/blueprints/morphos/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "morphos-server" port = 8080 @@ -11,9 +12,3 @@ certResolver = "letsencrypt" [config.env] # No environment variables required based on provided information - -[[config.mounts]] -source = "../files/morphos-upload" -target = "/upload" -type = "bind" -readOnly = false \ No newline at end of file diff --git a/blueprints/movary/docker-compose.yml b/blueprints/movary/docker-compose.yml index f84ff9e5d..692d1f0f6 100644 --- a/blueprints/movary/docker-compose.yml +++ b/blueprints/movary/docker-compose.yml @@ -2,7 +2,7 @@ services: movary: image: leepeuker/movary:${MOVARY_VERSION:-latest} restart: unless-stopped - ports: + expose: - "8080" environment: # TMDB API configuration diff --git a/blueprints/mulesoft-esb/docker-compose.yml b/blueprints/mulesoft-esb/docker-compose.yml index 6691e4554..c9af933dc 100644 --- a/blueprints/mulesoft-esb/docker-compose.yml +++ b/blueprints/mulesoft-esb/docker-compose.yml @@ -4,7 +4,7 @@ services: mule: image: yogeshmulecraft/mulece-esb:latest restart: unless-stopped - ports: + expose: - "8081" environment: - MULE_VERSION=${MULE_VERSION:-4.9.0} diff --git a/blueprints/mumble/docker-compose.yml b/blueprints/mumble/docker-compose.yml index 92580f1aa..8f7a591e7 100644 --- a/blueprints/mumble/docker-compose.yml +++ b/blueprints/mumble/docker-compose.yml @@ -2,7 +2,7 @@ services: mumble-server: image: mumblevoip/mumble-server:latest restart: unless-stopped - ports: + expose: - 64738 - 64738/udp volumes: diff --git a/blueprints/navi-music/docker-compose.yml b/blueprints/navi-music/docker-compose.yml new file mode 100644 index 000000000..d0fd9543c --- /dev/null +++ b/blueprints/navi-music/docker-compose.yml @@ -0,0 +1,31 @@ +services: + navi-music: + # 4.1.0 is the newest release that can boot without a YouTube OAuth2 refresh + # token; 4.1.1+ throws at startup ("YouTube OAuth token is mandatory") unless + # a valid token obtained via an interactive device-code flow is provided, so + # newer tags can never boot with template defaults. + image: anvian/navi-music:4.1.0 + restart: unless-stopped + # NaviMusic logs (rather than throws) when DISCORD_TOKEN is empty or invalid, + # then exits 0 because no JDA threads keep the JVM alive. Keep the container + # running in that case so first-time users see the instructions in the logs + # instead of a restart loop; real crashes (non-zero exits) still propagate to + # the restart policy. + entrypoint: + - sh + - -c + - >- + java $$JAVA_OPTS -jar /app/NaviMusic.jar nogui --spring.profiles.active=prod; + code=$$?; + if [ "$$code" -eq 0 ]; then + echo "NaviMusic is idle. Set DISCORD_TOKEN (and optionally Spotify/YouTube credentials) in the Environment tab, then redeploy."; + tail -f /dev/null; + fi; + exit $$code + environment: + DISCORD_TOKEN: ${DISCORD_TOKEN} + SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID} + SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET} + YOUTUBE_POTOKEN: ${YOUTUBE_POTOKEN} + YOUTUBE_VISITOR: ${YOUTUBE_VISITOR} + YOUTUBE_OAUTH2: ${YOUTUBE_OAUTH2} diff --git a/blueprints/navi-music/meta.json b/blueprints/navi-music/meta.json new file mode 100644 index 000000000..a5aefed5b --- /dev/null +++ b/blueprints/navi-music/meta.json @@ -0,0 +1,17 @@ +{ + "id": "navi-music", + "name": "NaviMusic", + "version": "4.1.0", + "description": "A Discord music bot that plays YouTube and Spotify tracks and playlists in voice channels.", + "logo": "navi-music.png", + "links": { + "github": "https://github.com/andre-carbajal/NaviMusic", + "website": "https://github.com/andre-carbajal/NaviMusic", + "docs": "https://github.com/andre-carbajal/NaviMusic#configuration" + }, + "tags": [ + "discord", + "music", + "bot" + ] +} diff --git a/blueprints/navi-music/navi-music.png b/blueprints/navi-music/navi-music.png new file mode 100755 index 000000000..ec554749a Binary files /dev/null and b/blueprints/navi-music/navi-music.png differ diff --git a/blueprints/navi-music/template.toml b/blueprints/navi-music/template.toml new file mode 100644 index 000000000..cc4a40cce --- /dev/null +++ b/blueprints/navi-music/template.toml @@ -0,0 +1,12 @@ +[config] +env = [ + # Required: create a Discord bot and paste its token. + "DISCORD_TOKEN=", + # Optional but recommended for Spotify links: https://developer.spotify.com/dashboard + "SPOTIFY_CLIENT_ID=", + "SPOTIFY_CLIENT_SECRET=", + # Optional YouTube tweaks (leave empty to use the default anonymous clients). + "YOUTUBE_POTOKEN=", + "YOUTUBE_VISITOR=", + "YOUTUBE_OAUTH2=", +] diff --git a/blueprints/navidrome/docker-compose.yml b/blueprints/navidrome/docker-compose.yml index 923b3d576..75d744407 100644 --- a/blueprints/navidrome/docker-compose.yml +++ b/blueprints/navidrome/docker-compose.yml @@ -9,9 +9,8 @@ services: volumes: - navidrome-data:/data - navidrome-music:/music:ro - ports: + expose: - 4533 - volumes: navidrome-data: {} - navidrome-music: {} \ No newline at end of file + navidrome-music: {} diff --git a/blueprints/navidrome/template.toml b/blueprints/navidrome/template.toml index 72b1d002e..bf8f0c26a 100644 --- a/blueprints/navidrome/template.toml +++ b/blueprints/navidrome/template.toml @@ -2,11 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "navidrome" port = 4533 host = "${main_domain}" [config.env] - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/naviserver/docker-compose.yml b/blueprints/naviserver/docker-compose.yml new file mode 100644 index 000000000..8b9807d0b --- /dev/null +++ b/blueprints/naviserver/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.8" + +services: + naviserver: + image: ghcr.io/andre-carbajal/naviserver:2.4.5 + restart: unless-stopped + expose: + - "23008" + ports: + - "25565-25600" + environment: + NAVISERVER_HOST: ${NAVISERVER_HOST} + NAVISERVER_PORT: ${NAVISERVER_PORT} + NAVISERVER_ALLOWED_ORIGINS: ${NAVISERVER_ALLOWED_ORIGINS} + NAVISERVER_SECRET_KEY: ${NAVISERVER_SECRET_KEY} + NAVISERVER_CLI_TOKEN: ${NAVISERVER_CLI_TOKEN} + CURSEFORGE_API_KEY: ${CURSEFORGE_API_KEY} + volumes: + - naviserver-data:/data +volumes: + naviserver-data: {} diff --git a/blueprints/naviserver/meta.json b/blueprints/naviserver/meta.json new file mode 100644 index 000000000..fadca7cd1 --- /dev/null +++ b/blueprints/naviserver/meta.json @@ -0,0 +1,18 @@ +{ + "id": "naviserver", + "name": "NaviServer", + "version": "2.4.5", + "description": "A lightweight Minecraft server manager with a web interface, CLI, automatic JVM management, monitoring, and support for Vanilla, Paper, Fabric, Forge, and NeoForge.", + "logo": "naviserver.png", + "links": { + "github": "https://github.com/andre-carbajal/NaviServer", + "website": "https://github.com/andre-carbajal/NaviServer", + "docs": "https://github.com/andre-carbajal/NaviServer/wiki" + }, + "tags": [ + "minecraft", + "game-server", + "self-hosted", + "management" + ] +} diff --git a/blueprints/naviserver/naviserver.png b/blueprints/naviserver/naviserver.png new file mode 100644 index 000000000..6fd5ee9e2 Binary files /dev/null and b/blueprints/naviserver/naviserver.png differ diff --git a/blueprints/naviserver/template.toml b/blueprints/naviserver/template.toml new file mode 100644 index 000000000..1f401e66b --- /dev/null +++ b/blueprints/naviserver/template.toml @@ -0,0 +1,19 @@ +[variables] +main_domain = "${domain}" +naviserver_secret_key = "${password:64}" +naviserver_cli_token = "${password:64}" + +[config] +env = [ + "NAVISERVER_HOST=0.0.0.0", + "NAVISERVER_PORT=23008", + "NAVISERVER_ALLOWED_ORIGINS=", + "NAVISERVER_SECRET_KEY=${naviserver_secret_key}", + "NAVISERVER_CLI_TOKEN=${naviserver_cli_token}", + "CURSEFORGE_API_KEY=", +] + +[[config.domains]] +serviceName = "naviserver" +port = 23008 +host = "${main_domain}" diff --git a/blueprints/neko/template.toml b/blueprints/neko/template.toml index 31060385b..47a223c39 100644 --- a/blueprints/neko/template.toml +++ b/blueprints/neko/template.toml @@ -4,6 +4,7 @@ admin_password = "${password:16}" user_password = "${password:16}" [config] +mounts = [] [[config.domains]] serviceName = "neko" port = 8080 @@ -18,5 +19,3 @@ NEKO_MEMBER_MULTIUSER_USER_PASSWORD = "${user_password}" NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD = "${admin_password}" NEKO_WEBRTC_EPR = "52000-52100" NEKO_WEBRTC_ICELITE = "1" - -[[config.mounts]] diff --git a/blueprints/netdata/docker-compose.yml b/blueprints/netdata/docker-compose.yml index 36f6a736b..a9cd5bb0c 100644 --- a/blueprints/netdata/docker-compose.yml +++ b/blueprints/netdata/docker-compose.yml @@ -22,9 +22,8 @@ services: - NETDATA_CLAIM_TOKEN=${NETDATA_CLAIM_TOKEN:-} - NETDATA_CLAIM_URL=${NETDATA_CLAIM_URL:-} - NETDATA_CLAIM_ROOMS=${NETDATA_CLAIM_ROOMS:-} - ports: + expose: - "19999" - volumes: netdata-config: netdata-lib: diff --git a/blueprints/nginx/docker-compose.yml b/blueprints/nginx/docker-compose.yml index 85e6c4c66..ca4c56c5f 100644 --- a/blueprints/nginx/docker-compose.yml +++ b/blueprints/nginx/docker-compose.yml @@ -3,7 +3,7 @@ services: nginx: image: nginx:latest restart: unless-stopped - ports: + expose: - 80 - 443 volumes: @@ -11,4 +11,4 @@ services: - nginx-html:/usr/share/nginx/html volumes: nginx-config: {} - nginx-html: {} \ No newline at end of file + nginx-html: {} diff --git a/blueprints/nocodb/template.toml b/blueprints/nocodb/template.toml index ab24e1e2b..fe7f432f4 100644 --- a/blueprints/nocodb/template.toml +++ b/blueprints/nocodb/template.toml @@ -5,6 +5,7 @@ postgres_password = "${password:32}" postgres_db = "root_db" [config] +mounts = [] [[config.domains]] serviceName = "nocodb" port = 8080 @@ -14,13 +15,3 @@ host = "${main_domain}" POSTGRES_USER = "${postgres_user}" POSTGRES_PASSWORD = "${postgres_password}" POSTGRES_DB = "${postgres_db}" - -[[config.mounts]] -serviceName = "nocodb" -volumeName = "nc_data" -mountPath = "/usr/app/data" - -[[config.mounts]] -serviceName = "root_db" -volumeName = "db_data" -mountPath = "/var/lib/postgresql/data" diff --git a/blueprints/novu/docker-compose.yml b/blueprints/novu/docker-compose.yml new file mode 100644 index 000000000..39be0481a --- /dev/null +++ b/blueprints/novu/docker-compose.yml @@ -0,0 +1,205 @@ +version: "3.8" + +services: + redis: + image: redis:7-alpine + restart: unless-stopped + command: redis-server --appendonly yes + volumes: + - novu-redis:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + mongodb: + image: mongo:8.0.17 + restart: unless-stopped + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} + volumes: + - novu-mongodb:/data/db + healthcheck: + test: + [ + "CMD", + "mongosh", + "--quiet", + "--username", + "${MONGO_INITDB_ROOT_USERNAME}", + "--password", + "${MONGO_INITDB_ROOT_PASSWORD}", + "--eval", + "db.adminCommand('ping').ok", + ] + interval: 20s + timeout: 5s + retries: 5 + start_period: 20s + + api: + image: ghcr.io/novuhq/novu/api:3.18.0 + restart: unless-stopped + depends_on: + mongodb: + condition: service_healthy + redis: + condition: service_healthy + environment: + NODE_ENV: ${NODE_ENV} + API_ROOT_URL: ${API_ROOT_URL} + PORT: ${API_PORT} + FRONT_BASE_URL: ${FRONT_BASE_URL} + MONGO_URL: ${MONGO_URL} + MONGO_MIN_POOL_SIZE: ${MONGO_MIN_POOL_SIZE} + MONGO_MAX_POOL_SIZE: ${MONGO_MAX_POOL_SIZE} + MONGO_AUTO_CREATE_INDEXES: ${MONGO_AUTO_CREATE_INDEXES} + REDIS_HOST: ${REDIS_HOST} + REDIS_PORT: ${REDIS_PORT} + REDIS_PASSWORD: ${REDIS_PASSWORD} + REDIS_DB_INDEX: 2 + REDIS_CACHE_SERVICE_HOST: ${REDIS_CACHE_SERVICE_HOST} + REDIS_CACHE_SERVICE_PORT: ${REDIS_CACHE_SERVICE_PORT} + S3_LOCAL_STACK: ${S3_LOCAL_STACK} + S3_BUCKET_NAME: ${S3_BUCKET_NAME} + S3_REGION: ${S3_REGION} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + JWT_SECRET: ${JWT_SECRET} + STORE_ENCRYPTION_KEY: ${STORE_ENCRYPTION_KEY} + NOVU_SECRET_KEY: ${NOVU_SECRET_KEY} + SUBSCRIBER_WIDGET_JWT_EXPIRATION_TIME: ${SUBSCRIBER_WIDGET_JWT_EXPIRATION_TIME} + SENTRY_DSN: ${SENTRY_DSN} + NEW_RELIC_ENABLED: ${NEW_RELIC_ENABLED} + NEW_RELIC_APP_NAME: ${NEW_RELIC_APP_NAME} + NEW_RELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY} + API_CONTEXT_PATH: ${API_CONTEXT_PATH:-} + IS_API_IDEMPOTENCY_ENABLED: ${IS_API_IDEMPOTENCY_ENABLED} + IS_API_RATE_LIMITING_ENABLED: ${IS_API_RATE_LIMITING_ENABLED} + IS_NEW_MESSAGES_API_RESPONSE_ENABLED: ${IS_NEW_MESSAGES_API_RESPONSE_ENABLED} + IS_V2_ENABLED: ${IS_V2_ENABLED} + IS_SELF_HOSTED: ${IS_SELF_HOSTED} + expose: + - "3000" + healthcheck: + test: + [ + "CMD-SHELL", + "wget --no-verbose --tries=1 --spider http://localhost:$${PORT}/v1/health-check || exit 1", + ] + interval: 20s + timeout: 10s + retries: 3 + start_period: 40s + + worker: + image: ghcr.io/novuhq/novu/worker:3.18.0 + restart: unless-stopped + depends_on: + mongodb: + condition: service_healthy + redis: + condition: service_healthy + environment: + NODE_ENV: ${NODE_ENV} + PORT: ${WORKER_PORT:-3004} + MONGO_URL: ${MONGO_URL} + MONGO_MIN_POOL_SIZE: ${MONGO_MIN_POOL_SIZE} + MONGO_MAX_POOL_SIZE: ${MONGO_MAX_POOL_SIZE} + REDIS_HOST: ${REDIS_HOST} + REDIS_PORT: ${REDIS_PORT} + REDIS_PASSWORD: ${REDIS_PASSWORD} + REDIS_DB_INDEX: 2 + REDIS_CACHE_SERVICE_HOST: ${REDIS_CACHE_SERVICE_HOST} + REDIS_CACHE_SERVICE_PORT: ${REDIS_CACHE_SERVICE_PORT} + S3_LOCAL_STACK: ${S3_LOCAL_STACK} + S3_BUCKET_NAME: ${S3_BUCKET_NAME} + S3_REGION: ${S3_REGION} + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} + STORE_ENCRYPTION_KEY: ${STORE_ENCRYPTION_KEY} + SUBSCRIBER_WIDGET_JWT_EXPIRATION_TIME: ${SUBSCRIBER_WIDGET_JWT_EXPIRATION_TIME} + SENTRY_DSN: ${SENTRY_DSN} + NEW_RELIC_ENABLED: ${NEW_RELIC_ENABLED} + NEW_RELIC_APP_NAME: ${NEW_RELIC_APP_NAME} + NEW_RELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY} + BROADCAST_QUEUE_CHUNK_SIZE: ${BROADCAST_QUEUE_CHUNK_SIZE} + MULTICAST_QUEUE_CHUNK_SIZE: ${MULTICAST_QUEUE_CHUNK_SIZE} + API_ROOT_URL: http://api:${API_PORT} + IS_EMAIL_INLINE_CSS_DISABLED: ${IS_EMAIL_INLINE_CSS_DISABLED} + IS_USE_MERGED_DIGEST_ID_ENABLED: ${IS_USE_MERGED_DIGEST_ID_ENABLED} + healthcheck: + test: + [ + "CMD-SHELL", + "wget --no-verbose --tries=1 --spider http://localhost:$${PORT:-3004}/v1/health-check || exit 1", + ] + interval: 20s + timeout: 10s + retries: 3 + start_period: 20s + + ws: + image: ghcr.io/novuhq/novu/ws:3.18.0 + restart: unless-stopped + depends_on: + mongodb: + condition: service_healthy + redis: + condition: service_healthy + environment: + PORT: ${WS_PORT} + NODE_ENV: ${NODE_ENV} + MONGO_URL: ${MONGO_URL} + MONGO_MIN_POOL_SIZE: ${MONGO_MIN_POOL_SIZE} + MONGO_MAX_POOL_SIZE: ${MONGO_MAX_POOL_SIZE} + REDIS_HOST: ${REDIS_HOST} + REDIS_PORT: ${REDIS_PORT} + REDIS_PASSWORD: ${REDIS_PASSWORD} + JWT_SECRET: ${JWT_SECRET} + WS_CONTEXT_PATH: ${WS_CONTEXT_PATH:-} + NEW_RELIC_ENABLED: ${NEW_RELIC_ENABLED} + NEW_RELIC_APP_NAME: ${NEW_RELIC_APP_NAME} + NEW_RELIC_LICENSE_KEY: ${NEW_RELIC_LICENSE_KEY} + expose: + - "3002" + healthcheck: + test: + [ + "CMD-SHELL", + "wget --no-verbose --tries=1 --spider http://localhost:$${PORT}/v1/health-check || exit 1", + ] + interval: 20s + timeout: 10s + retries: 3 + start_period: 40s + + novu: + image: ghcr.io/novuhq/novu/dashboard:3.18.0 + restart: unless-stopped + depends_on: + api: + condition: service_healthy + worker: + condition: service_healthy + environment: + VITE_API_HOSTNAME: ${VITE_API_HOSTNAME} + VITE_WEBSOCKET_HOSTNAME: ${VITE_WEBSOCKET_HOSTNAME} + expose: + - "4000" + healthcheck: + test: + [ + "CMD-SHELL", + 'node -e "const http = require(''http''); const req = http.get({hostname: ''localhost'', port: 4000, path: ''/'', timeout: 5000}, (res) => { process.exit(res.statusCode === 200 ? 0 : 1); }); req.on(''error'', () => process.exit(1)); req.on(''timeout'', () => { req.destroy(); process.exit(1); });"', + ] + interval: 20s + timeout: 10s + retries: 3 + start_period: 20s + +volumes: + novu-mongodb: + novu-redis: diff --git a/blueprints/novu/meta.json b/blueprints/novu/meta.json new file mode 100644 index 000000000..01283d5c0 --- /dev/null +++ b/blueprints/novu/meta.json @@ -0,0 +1,21 @@ +{ + "id": "novu", + "name": "Novu", + "version": "3.18.0", + "description": "Open-source notification infrastructure for multi-channel workflows — in-app Inbox, email, SMS, push, and chat — with a unified API and dashboard.", + "logo": "novu.png", + "links": { + "github": "https://github.com/novuhq/novu", + "website": "https://novu.co/", + "docs": "https://docs.novu.co/community/self-hosting-novu/deploy-with-docker" + }, + "tags": [ + "notifications", + "email", + "sms", + "push", + "inbox", + "self-hosted", + "api" + ] +} diff --git a/blueprints/novu/novu.png b/blueprints/novu/novu.png new file mode 100644 index 000000000..610a875bd Binary files /dev/null and b/blueprints/novu/novu.png differ diff --git a/blueprints/novu/template.toml b/blueprints/novu/template.toml new file mode 100644 index 000000000..0a39576f2 --- /dev/null +++ b/blueprints/novu/template.toml @@ -0,0 +1,129 @@ +[variables] +main_domain = "${domain}" +api_domain = "${domain}" +ws_domain = "${domain}" +jwt_secret = "${password:64}" +# STORE_ENCRYPTION_KEY must be exactly 32 characters +store_encryption_key = "${password:32}" +novu_secret_key = "${password:64}" +mongo_password = "${password:32}" +mongo_user = "root" + +[config] +mounts = [] + +env = [ + "###########################################", + "# Secrets", + "# JWT_SECRET / NOVU_SECRET_KEY: auto-generated — keep as-is unless rotating", + "# STORE_ENCRYPTION_KEY: must be exactly 32 characters", + "###########################################", + "JWT_SECRET=${jwt_secret}", + "STORE_ENCRYPTION_KEY=${store_encryption_key}", + "NOVU_SECRET_KEY=${novu_secret_key}", + "SUBSCRIBER_WIDGET_JWT_EXPIRATION_TIME=15d", + "", + "###########################################", + "# General / Feature Flags", + "###########################################", + "NODE_ENV=production", + "IS_SELF_HOSTED=true", + "IS_V2_ENABLED=true", + "IS_API_IDEMPOTENCY_ENABLED=false", + "IS_API_RATE_LIMITING_ENABLED=false", + "IS_NEW_MESSAGES_API_RESPONSE_ENABLED=true", + "IS_EMAIL_INLINE_CSS_DISABLED=false", + "IS_USE_MERGED_DIGEST_ID_ENABLED=false", + "", + "###########################################", + "# Public Service URLs", + "# Assign 3 domains in Dokploy Domains tab:", + "# FRONT_BASE_URL -> novu service (port 4000) — Dashboard", + "# API_ROOT_URL / VITE_* -> api service (port 3000) — REST API", + "# VITE_WEBSOCKET_HOSTNAME -> ws service (port 3002) — WebSocket", + "###########################################", + "FRONT_BASE_URL=https://${main_domain}", + "API_ROOT_URL=https://${api_domain}", + "VITE_API_HOSTNAME=https://${api_domain}", + "VITE_WEBSOCKET_HOSTNAME=https://${ws_domain}", + "", + "###########################################", + "# Ports (internal — do not change unless you know why)", + "###########################################", + "API_PORT=3000", + "WS_PORT=3002", + "WORKER_PORT=3004", + "", + "###########################################", + "# MongoDB", + "###########################################", + "MONGO_INITDB_ROOT_USERNAME=${mongo_user}", + "MONGO_INITDB_ROOT_PASSWORD=${mongo_password}", + "MONGO_URL=mongodb://${mongo_user}:${mongo_password}@mongodb:27017/novu-db?authSource=admin", + "MONGO_AUTO_CREATE_INDEXES=true", + "MONGO_MIN_POOL_SIZE=5", + "MONGO_MAX_POOL_SIZE=10", + "", + "###########################################", + "# Redis (queue + cache — shared for small deployments)", + "###########################################", + "REDIS_HOST=redis", + "REDIS_PORT=6379", + "REDIS_PASSWORD=", + "REDIS_CACHE_SERVICE_HOST=redis", + "REDIS_CACHE_SERVICE_PORT=6379", + "", + "###########################################", + "# S3 / Object Storage (optional)", + "# NOT local disk — Novu has no built-in filesystem storage.", + "# S3_LOCAL_STACK = your S3/MinIO endpoint URL (Novu's official env name).", + "# Example MinIO: S3_LOCAL_STACK=https://minio.yourdomain.com", + "# Example LocalStack: S3_LOCAL_STACK=http://localhost:4566", + "# Leave empty + test keys for basic deploy (dashboard/API still work).", + "# When you need uploads, set real endpoint + bucket + AWS_* credentials.", + "###########################################", + "S3_LOCAL_STACK=", + "S3_BUCKET_NAME=novu-local", + "S3_REGION=us-east-1", + "AWS_ACCESS_KEY_ID=test", + "AWS_SECRET_ACCESS_KEY=test", + "", + "###########################################", + "# Context Paths (path-based reverse proxy only)", + "# Leave empty for normal subdomain routing in Dokploy", + "###########################################", + "API_CONTEXT_PATH=", + "WS_CONTEXT_PATH=", + "", + "###########################################", + "# Analytics / Observability (disabled by default)", + "###########################################", + "SENTRY_DSN=", + "NEW_RELIC_ENABLED=false", + "NEW_RELIC_APP_NAME=", + "NEW_RELIC_LICENSE_KEY=", + "", + "###########################################", + "# Worker Tuning", + "###########################################", + "BROADCAST_QUEUE_CHUNK_SIZE=100", + "MULTICAST_QUEUE_CHUNK_SIZE=100", +] + +# Dashboard UI +[[config.domains]] +serviceName = "novu" +port = 4_000 +host = "${main_domain}" + +# REST API +[[config.domains]] +serviceName = "api" +port = 3_000 +host = "${api_domain}" + +# WebSocket service (Inbox / real-time) +[[config.domains]] +serviceName = "ws" +port = 3_002 +host = "${ws_domain}" diff --git a/blueprints/ntfy/docker-compose.yml b/blueprints/ntfy/docker-compose.yml index ac68ff31b..c43df5d5c 100644 --- a/blueprints/ntfy/docker-compose.yml +++ b/blueprints/ntfy/docker-compose.yml @@ -4,7 +4,7 @@ services: restart: unless-stopped command: - serve - ports: + expose: - "${HTTP_PORT}" volumes: - ntfy-data:/var/lib/ntfy diff --git a/blueprints/obsidian-livesync/docker-compose.yml b/blueprints/obsidian-livesync/docker-compose.yml index fbe57ae56..88a57d2d4 100644 --- a/blueprints/obsidian-livesync/docker-compose.yml +++ b/blueprints/obsidian-livesync/docker-compose.yml @@ -9,8 +9,7 @@ services: volumes: - couchdb-data:/opt/couchdb/data - ../files/local.ini:/opt/couchdb/etc/local.ini - ports: + expose: - 5984 - volumes: couchdb-data: {} diff --git a/blueprints/ojs/docker-compose.yml b/blueprints/ojs/docker-compose.yml index 72aba02e1..19b922869 100644 --- a/blueprints/ojs/docker-compose.yml +++ b/blueprints/ojs/docker-compose.yml @@ -15,7 +15,7 @@ services: ojs: image: "pkpofficial/ojs:3_3_0-21" hostname: "${COMPOSE_PROJECT_NAME}" - ports: + expose: - 80 - 443 volumes: diff --git a/blueprints/ollama-chat-tone/docker-compose.yml b/blueprints/ollama-chat-tone/docker-compose.yml index 8605f824c..28da0149c 100644 --- a/blueprints/ollama-chat-tone/docker-compose.yml +++ b/blueprints/ollama-chat-tone/docker-compose.yml @@ -1,7 +1,7 @@ services: ollama-chat-tone: image: billnice250/chattone:latest - ports: + expose: - 8080 environment: APP_NAME: "Ollama Chat Tone" diff --git a/blueprints/omni-tools/docker-compose.yml b/blueprints/omni-tools/docker-compose.yml index 166cc6364..722ec84ab 100644 --- a/blueprints/omni-tools/docker-compose.yml +++ b/blueprints/omni-tools/docker-compose.yml @@ -2,5 +2,5 @@ services: omni-tools: image: iib0011/omni-tools:latest restart: unless-stopped - ports: + expose: - 80 diff --git a/blueprints/omni-tools/template.toml b/blueprints/omni-tools/template.toml index 0c34442e8..c46c85926 100644 --- a/blueprints/omni-tools/template.toml +++ b/blueprints/omni-tools/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "omni-tools" port = 80 @@ -10,5 +11,3 @@ host = "${main_domain}" [config.env] # API Key LOCIZE_API_KEY = "" - -[[config.mounts]] diff --git a/blueprints/onedev/docker-compose.yml b/blueprints/onedev/docker-compose.yml index af4122cf8..8ef6a2aff 100644 --- a/blueprints/onedev/docker-compose.yml +++ b/blueprints/onedev/docker-compose.yml @@ -9,4 +9,4 @@ services: - "onedev-data:/opt/onedev" volumes: - onedev-data: \ No newline at end of file + onedev-data: diff --git a/blueprints/ontime/docker-compose.yml b/blueprints/ontime/docker-compose.yml index 2c04bcb3f..4ab1d1831 100644 --- a/blueprints/ontime/docker-compose.yml +++ b/blueprints/ontime/docker-compose.yml @@ -1,7 +1,7 @@ services: ontime: image: getontime/ontime:v3.8.0 - ports: + expose: - 4001 - 8888 - 9999 diff --git a/blueprints/open-design/README.md b/blueprints/open-design/README.md new file mode 100644 index 000000000..0afd0397d --- /dev/null +++ b/blueprints/open-design/README.md @@ -0,0 +1,19 @@ +# Open Design + +Local-first, open-source design tool with native desktop apps, a large library of +design systems, and an extensible plugin ecosystem. + +## Notes + +- **Access / security.** This template sets `OD_DISABLE_API_AUTH=1`. The Open Design + daemon token-gates its API when bound to a non-loopback address, and a browser + cannot supply that bearer token — so disabling it is required for the web UI to + load. As a result the instance is **reachable by anyone who has the URL**. If it is + internet-facing, put an authenticating layer in front (for example Dokploy/Traefik + basic-auth) and enable an HTTPS certificate on the domain. + +- **AI features.** The image intentionally does not bundle an AI agent CLI. To + generate designs, either **Sign in to Open Design** (cloud) or choose **Bring your + own key** and provide a provider API key (e.g. `ANTHROPIC_API_KEY`, + `OPENAI_API_KEY`). A "vela binary not found" notice on the sign-in screen is + expected and only affects the cloud-agent path. diff --git a/blueprints/open-design/docker-compose.yml b/blueprints/open-design/docker-compose.yml new file mode 100644 index 000000000..709b4da0c --- /dev/null +++ b/blueprints/open-design/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.8" + +services: + open-design: + image: ghcr.io/nexu-io/od:0.16.1 + restart: unless-stopped + environment: + NODE_ENV: production + NODE_OPTIONS: --max-old-space-size=192 + OD_BIND_HOST: 0.0.0.0 + OD_PORT: 7456 + OD_API_TOKEN: ${OD_API_TOKEN} + OD_ALLOWED_ORIGINS: ${OD_ALLOWED_ORIGINS} + # The daemon token-gates its API when bound to 0.0.0.0, and a browser + # cannot present that token, so the web UI needs API auth disabled to + # load. The instance is then reachable by anyone with the URL — front it + # with auth/HTTPS if it is internet-facing (see README.md). + OD_DISABLE_API_AUTH: "1" + volumes: + - open_design_data:/app/.od + healthcheck: + test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:7456/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 20s + +volumes: + open_design_data: {} diff --git a/blueprints/open-design/meta.json b/blueprints/open-design/meta.json new file mode 100644 index 000000000..b3c437398 --- /dev/null +++ b/blueprints/open-design/meta.json @@ -0,0 +1,16 @@ +{ + "id": "open-design", + "name": "Open Design", + "version": "0.16.1", + "description": "Open Design is a local-first, open-source design tool with native desktop apps, a large library of design systems, and an extensible plugin ecosystem that runs across many coding agents.", + "logo": "open-design.svg", + "links": { + "github": "https://github.com/nexu-io/open-design", + "website": "https://open-design.ai", + "docs": "https://open-design.ai" + }, + "tags": [ + "design", + "self-hosted" + ] +} diff --git a/blueprints/open-design/open-design.svg b/blueprints/open-design/open-design.svg new file mode 100644 index 000000000..19980f0fa --- /dev/null +++ b/blueprints/open-design/open-design.svg @@ -0,0 +1,3 @@ + + + diff --git a/blueprints/open-design/template.toml b/blueprints/open-design/template.toml new file mode 100644 index 000000000..1bfd9b3a1 --- /dev/null +++ b/blueprints/open-design/template.toml @@ -0,0 +1,15 @@ +[variables] +main_domain = "${domain}" +api_token = "${password:32}" + +[config] +env = [ + "OD_API_TOKEN=${api_token}", + "OD_ALLOWED_ORIGINS=https://${main_domain},http://${main_domain}", +] +mounts = [] + +[[config.domains]] +serviceName = "open-design" +port = 7456 +host = "${main_domain}" diff --git a/blueprints/open-webui/docker-compose.yml b/blueprints/open-webui/docker-compose.yml index bde3a1a0a..a18caa3c0 100644 --- a/blueprints/open-webui/docker-compose.yml +++ b/blueprints/open-webui/docker-compose.yml @@ -3,7 +3,7 @@ services: open-webui: image: ghcr.io/open-webui/open-webui:main restart: unless-stopped - ports: + expose: - 8080 environment: # This should point to your Ollama instance. diff --git a/blueprints/open-webui/template.toml b/blueprints/open-webui/template.toml index 730d5c4ea..76453852f 100644 --- a/blueprints/open-webui/template.toml +++ b/blueprints/open-webui/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" webui_secret_key = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "open-webui" port = 8080 @@ -12,17 +13,3 @@ host = "${main_domain}" # Secret key for signing JWTs for authentication. # A random key is generated by default. webui_secret_key = "${webui_secret_key}" # API Key - -[[config.mounts]] -serviceName = "open-webui" -type = "volume" -source = "open-webui" -target = "/app/backend/data" - -# Mount for the optional 'ollama' service. -# This will be used if you uncomment the 'ollama' service in the docker-compose file. -[[config.mounts]] -serviceName = "ollama" -type = "volume" -source = "ollama" -target = "/root/.ollama" \ No newline at end of file diff --git a/blueprints/open_notebook/docker-compose.yml b/blueprints/open_notebook/docker-compose.yml index 3cd09fdb9..b0a1b3453 100644 --- a/blueprints/open_notebook/docker-compose.yml +++ b/blueprints/open_notebook/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: surrealdb: image: surrealdb/surrealdb:v2 - ports: + expose: - 8000 volumes: - ../files/surreal_data:/mydata @@ -12,7 +12,7 @@ services: open_notebook: image: lfnovo/open_notebook:latest - ports: + expose: - 8502 environment: - SURREAL_URL=ws://surrealdb:8000/rpc diff --git a/blueprints/opengist/docker-compose.yml b/blueprints/opengist/docker-compose.yml index a321052ab..8ccc17954 100644 --- a/blueprints/opengist/docker-compose.yml +++ b/blueprints/opengist/docker-compose.yml @@ -4,7 +4,7 @@ services: opengist: image: ghcr.io/thomiceli/opengist:1 restart: unless-stopped - ports: + expose: - 6157 # HTTP port - 2222 # SSH port (optional) volumes: diff --git a/blueprints/opengist/template.toml b/blueprints/opengist/template.toml index a974ffc5d..76c01e89f 100644 --- a/blueprints/opengist/template.toml +++ b/blueprints/opengist/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "opengist" port = 6157 @@ -12,7 +13,3 @@ host = "${main_domain}" UID = "1001" GID = "1001" OG_LOG_LEVEL = "info" - -[[config.mounts]] -# This template uses a named volume defined in the docker-compose.yml, -# so no file mounts need to be configured here. diff --git a/blueprints/openhabittracker/docker-compose.yml b/blueprints/openhabittracker/docker-compose.yml index 31cebec51..e99470bf5 100644 --- a/blueprints/openhabittracker/docker-compose.yml +++ b/blueprints/openhabittracker/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: openhabittracker: image: jinjinov/openhabittracker:latest - ports: + expose: - '8080' environment: AppSettings__UserName: ${OHT_USERNAME} diff --git a/blueprints/openhands/docker-compose.yml b/blueprints/openhands/docker-compose.yml index 03d5048a4..be5e7d40b 100644 --- a/blueprints/openhands/docker-compose.yml +++ b/blueprints/openhands/docker-compose.yml @@ -7,10 +7,8 @@ services: restart: unless-stopped # The port is exposed without mapping. Dokploy handles the routing via the domain. - ports: + expose: - "3000" - - # Environment variables are sourced from the template.toml file. environment: - SANDBOX_RUNTIME_CONTAINER_IMAGE=${SANDBOX_RUNTIME_CONTAINER_IMAGE} - WORKSPACE_MOUNT_PATH=/opt/workspace_base diff --git a/blueprints/openinary/docker-compose.yml b/blueprints/openinary/docker-compose.yml index 0469c72b9..2452f7539 100644 --- a/blueprints/openinary/docker-compose.yml +++ b/blueprints/openinary/docker-compose.yml @@ -23,4 +23,4 @@ services: volumes: cache-data: public-files: - db-data: \ No newline at end of file + db-data: diff --git a/blueprints/openinary/template.toml b/blueprints/openinary/template.toml index ea317117d..f74c9a5e1 100644 --- a/blueprints/openinary/template.toml +++ b/blueprints/openinary/template.toml @@ -7,6 +7,7 @@ allowed_origin = "http://${main_domain}" next_public_api_base_url = "/api" [config] +mounts = [] [[config.domains]] serviceName = "openinary" port = 3000 @@ -18,23 +19,3 @@ BETTER_AUTH_SECRET = "${better_auth_secret}" BETTER_AUTH_URL = "${better_auth_url}" ALLOWED_ORIGIN = "${allowed_origin}" NEXT_PUBLIC_API_BASE_URL = "${next_public_api_base_url}" - -[[config.mounts]] -serviceName = "openinary" -volumeName = "cache-data" -mountPath = "/app/apps/api/cache" - -[[config.mounts]] -serviceName = "openinary" -volumeName = "public-files" -mountPath = "/app/apps/api/public" - -[[config.mounts]] -serviceName = "openinary" -volumeName = "db-data" -mountPath = "/app/data" - -[[config.mounts]] -serviceName = "openinary" -volumeName = "db-data" -mountPath = "/app/web-standalone/data" \ No newline at end of file diff --git a/blueprints/openspeedtest/docker-compose.yml b/blueprints/openspeedtest/docker-compose.yml index 0f35540bc..e85da62dd 100644 --- a/blueprints/openspeedtest/docker-compose.yml +++ b/blueprints/openspeedtest/docker-compose.yml @@ -2,5 +2,5 @@ services: openspeedtest: image: openspeedtest/latest:latest restart: unless-stopped - ports: - - 3000 \ No newline at end of file + expose: + - 3000 diff --git a/blueprints/openspeedtest/template.toml b/blueprints/openspeedtest/template.toml index 781fa32b3..f328a9e0c 100644 --- a/blueprints/openspeedtest/template.toml +++ b/blueprints/openspeedtest/template.toml @@ -2,11 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "openspeedtest" port = 3000 host = "${main_domain}" [config.env] - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/oryx/docker-compose.yml b/blueprints/oryx/docker-compose.yml index 98d460ff3..0fed0c3ec 100644 --- a/blueprints/oryx/docker-compose.yml +++ b/blueprints/oryx/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — RTMP (1935/tcp), WebRTC (8000/udp) and SRT (10080/udp) are streaming protocols Traefik cannot route; they must be published on the host. version: "3.8" services: oryx: diff --git a/blueprints/outline/docker-compose.yml b/blueprints/outline/docker-compose.yml index 9f7260404..387836f09 100644 --- a/blueprints/outline/docker-compose.yml +++ b/blueprints/outline/docker-compose.yml @@ -6,7 +6,7 @@ services: - postgres - redis - dex - ports: + expose: - 3000 environment: NODE_ENV: production @@ -38,9 +38,8 @@ services: - dex-server volumes: - ../files/etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf - ports: + expose: - 5556 - dex-server: image: ghcr.io/dexidp/dex:v2.37.0 restart: always diff --git a/blueprints/owncast/docker-compose.yml b/blueprints/owncast/docker-compose.yml index 08119dafb..d6867883a 100644 --- a/blueprints/owncast/docker-compose.yml +++ b/blueprints/owncast/docker-compose.yml @@ -7,9 +7,8 @@ services: restart: unless-stopped volumes: - owncast-data:/app/data - ports: + expose: - 8080 - 1935 - volumes: - owncast-data: {} \ No newline at end of file + owncast-data: {} diff --git a/blueprints/owncast/template.toml b/blueprints/owncast/template.toml index a428b7bd0..614efa72d 100644 --- a/blueprints/owncast/template.toml +++ b/blueprints/owncast/template.toml @@ -2,11 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "owncast" port = 8080 host = "${main_domain}" [config.env] - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/palmr/docker-compose.yml b/blueprints/palmr/docker-compose.yml index 5000310e9..a92948bbf 100644 --- a/blueprints/palmr/docker-compose.yml +++ b/blueprints/palmr/docker-compose.yml @@ -4,7 +4,7 @@ services: environment: - ENABLE_S3=false - ENCRYPTION_KEY=${ENCRYPTION_KEY} - ports: + expose: - "5487" - "3333" volumes: diff --git a/blueprints/palmr/template.toml b/blueprints/palmr/template.toml index ca953375c..a3b1deaa0 100644 --- a/blueprints/palmr/template.toml +++ b/blueprints/palmr/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "palmr" port = 5487 @@ -9,5 +10,3 @@ host = "${main_domain}" [config.env] ENCRYPTION_KEY = "" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/paperclip/docker-compose.yml b/blueprints/paperclip/docker-compose.yml index 5c1ce7bfe..7be91e977 100644 --- a/blueprints/paperclip/docker-compose.yml +++ b/blueprints/paperclip/docker-compose.yml @@ -41,4 +41,4 @@ services: volumes: postgres-data: {} - paperclip-data: {} \ No newline at end of file + paperclip-data: {} diff --git a/blueprints/parseable/docker-compose.yml b/blueprints/parseable/docker-compose.yml index 344ac6248..928cde2d8 100644 --- a/blueprints/parseable/docker-compose.yml +++ b/blueprints/parseable/docker-compose.yml @@ -13,7 +13,7 @@ services: - P_PASSWORD=${PARSEABLE_PASSWORD} - P_STAGING_DIR=/parseable/staging - P_FS_DIR=/parseable/data - ports: + expose: - 8000 - 8001 - 8002 diff --git a/blueprints/passbolt/template.toml b/blueprints/passbolt/template.toml index e14042e41..a8f9d4e9a 100644 --- a/blueprints/passbolt/template.toml +++ b/blueprints/passbolt/template.toml @@ -7,6 +7,7 @@ email_user = "noreply@example.com" email_pass = "${password:16}" [config] +mounts = [] [[config.domains]] serviceName = "passbolt" port = 80 @@ -27,15 +28,3 @@ PASSBOLT_EMAIL_PORT = "587" PASSBOLT_EMAIL_USER = "${email_user}" PASSBOLT_EMAIL_PASS = "${email_pass}" PASSBOLT_EMAIL_TLS = "true" - -[[config.mounts]] -volume = "gpg_volume" -target = "/etc/passbolt/gpg" - -[[config.mounts]] -volume = "jwt_volume" -target = "/etc/passbolt/jwt" - -[[config.mounts]] -volume = "passbolt_mariadb_data" -target = "/var/lib/mysql" \ No newline at end of file diff --git a/blueprints/paymenter/docker-compose.yml b/blueprints/paymenter/docker-compose.yml index 35a2ea5e1..21b63b98d 100644 --- a/blueprints/paymenter/docker-compose.yml +++ b/blueprints/paymenter/docker-compose.yml @@ -20,7 +20,7 @@ services: paymenter: image: ghcr.io/paymenter/paymenter:latest restart: unless-stopped - ports: + expose: - 80 depends_on: - database @@ -50,4 +50,4 @@ volumes: paymenter-storage: {} paymenter-logs: {} paymenter-public: {} - paymenter-redis: {} \ No newline at end of file + paymenter-redis: {} diff --git a/blueprints/paymenter/template.toml b/blueprints/paymenter/template.toml index 36790074a..02335d2c2 100644 --- a/blueprints/paymenter/template.toml +++ b/blueprints/paymenter/template.toml @@ -5,6 +5,7 @@ mysql_password = "${password:16}" mysql_root_password = "${password:20}" [config] +mounts = [] [[config.domains]] serviceName = "paymenter" port = 80 @@ -18,5 +19,3 @@ MYSQL_PASSWORD = "${mysql_password}" MYSQL_ROOT_PASSWORD = "${mysql_root_password}" MYSQL_DATABASE = "paymenter" MYSQL_USER = "paymenter" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/penpot/docker-compose.yml b/blueprints/penpot/docker-compose.yml index ba238bdfc..9677a9f10 100644 --- a/blueprints/penpot/docker-compose.yml +++ b/blueprints/penpot/docker-compose.yml @@ -35,10 +35,9 @@ services: penpot-frontend: image: "penpotapp/frontend:2.6.1" restart: always - ports: + expose: - 8080 - 9001 - volumes: - penpot_assets:/opt/data/assets @@ -181,27 +180,4 @@ services: restart: always expose: - '1025' - ports: - - 1080 - - - ## Example configuration of MiniIO (S3 compatible object storage service); If you don't - ## have preference, then just use filesystem, this is here just for the completeness. - - # minio: - # image: "minio/minio:latest" - # command: minio server /mnt/data --console-address ":9001" - # restart: always - # - # volumes: - # - "penpot_minio:/mnt/data" - # - # environment: - # - MINIO_ROOT_USER=minioadmin - # - MINIO_ROOT_PASSWORD=minioadmin - # - # ports: - # - 9000:9000 - # - 9001:9001 - - + - '1080' diff --git a/blueprints/peppermint/docker-compose.yml b/blueprints/peppermint/docker-compose.yml index a5af058f9..72b868287 100644 --- a/blueprints/peppermint/docker-compose.yml +++ b/blueprints/peppermint/docker-compose.yml @@ -31,4 +31,4 @@ services: SECRET: ${SECRET} volumes: - peppermint-postgres-data: \ No newline at end of file + peppermint-postgres-data: diff --git a/blueprints/photoprism/docker-compose.yml b/blueprints/photoprism/docker-compose.yml index 56793dbd3..08daa9b9d 100644 --- a/blueprints/photoprism/docker-compose.yml +++ b/blueprints/photoprism/docker-compose.yml @@ -1,76 +1,76 @@ -services: - photoprism: - image: photoprism/photoprism:latest - stop_grace_period: 10s - depends_on: - - mariadb - security_opt: - - seccomp:unconfined - - apparmor:unconfined - - environment: - PHOTOPRISM_ADMIN_USER: "admin" - PHOTOPRISM_ADMIN_PASSWORD: ${ADMIN_PASSWORD} - PHOTOPRISM_AUTH_MODE: "password" - PHOTOPRISM_SITE_URL: "http://localhost:2342/" - PHOTOPRISM_DISABLE_TLS: "false" - PHOTOPRISM_DEFAULT_TLS: "false" - PHOTOPRISM_ORIGINALS_LIMIT: 5000 # file size limit for originals in MB (increase for high-res video) - PHOTOPRISM_HTTP_COMPRESSION: "gzip" - PHOTOPRISM_LOG_LEVEL: "info" # log level: trace, debug, info, warning, error, fatal, or panic - PHOTOPRISM_READONLY: "false" - PHOTOPRISM_EXPERIMENTAL: "false" - PHOTOPRISM_DISABLE_CHOWN: "false" - PHOTOPRISM_DISABLE_WEBDAV: "false" - PHOTOPRISM_DISABLE_SETTINGS: "false" - PHOTOPRISM_DISABLE_TENSORFLOW: "false" - PHOTOPRISM_DISABLE_FACES: "false" - PHOTOPRISM_DISABLE_CLASSIFICATION: "false" - PHOTOPRISM_DISABLE_VECTORS: "false" - PHOTOPRISM_DISABLE_RAW: "false" - PHOTOPRISM_RAW_PRESETS: "false" - PHOTOPRISM_SIDECAR_YAML: "true" - PHOTOPRISM_BACKUP_ALBUMS: "true" - PHOTOPRISM_BACKUP_DATABASE: "true" - PHOTOPRISM_BACKUP_SCHEDULE: "daily" - PHOTOPRISM_INDEX_SCHEDULE: "" - PHOTOPRISM_AUTO_INDEX: 300 - PHOTOPRISM_AUTO_IMPORT: -1 - PHOTOPRISM_DETECT_NSFW: "false" - PHOTOPRISM_UPLOAD_NSFW: "true" - PHOTOPRISM_DATABASE_DRIVER: "mysql" - PHOTOPRISM_DATABASE_SERVER: "mariadb:3306" - PHOTOPRISM_DATABASE_NAME: "photoprism" - PHOTOPRISM_DATABASE_USER: "photoprism" - PHOTOPRISM_DATABASE_PASSWORD: "insecure" - PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App" - PHOTOPRISM_SITE_DESCRIPTION: "" - PHOTOPRISM_SITE_AUTHOR: "" - working_dir: - "/photoprism" - volumes: - - pictures:/photoprism/originals - - storage-data:/photoprism/storage - - mariadb: - image: mariadb:11 - restart: unless-stopped - stop_grace_period: 5s - - security_opt: - - seccomp:unconfined - - apparmor:unconfined - volumes: - - db-data:/var/lib/mysql - environment: - MARIADB_AUTO_UPGRADE: "1" - MARIADB_INITDB_SKIP_TZINFO: "1" - MARIADB_DATABASE: "photoprism" - MARIADB_USER: "photoprism" - MARIADB_PASSWORD: "insecure" - MARIADB_ROOT_PASSWORD: "insecure" - -volumes: - db-data: - storage-data: - pictures: \ No newline at end of file +services: + photoprism: + image: photoprism/photoprism:latest + stop_grace_period: 10s + depends_on: + - mariadb + security_opt: + - seccomp:unconfined + - apparmor:unconfined + + environment: + PHOTOPRISM_ADMIN_USER: "admin" + PHOTOPRISM_ADMIN_PASSWORD: ${ADMIN_PASSWORD} + PHOTOPRISM_AUTH_MODE: "password" + PHOTOPRISM_SITE_URL: "http://localhost:2342/" + PHOTOPRISM_DISABLE_TLS: "false" + PHOTOPRISM_DEFAULT_TLS: "false" + PHOTOPRISM_ORIGINALS_LIMIT: 5000 # file size limit for originals in MB (increase for high-res video) + PHOTOPRISM_HTTP_COMPRESSION: "gzip" + PHOTOPRISM_LOG_LEVEL: "info" # log level: trace, debug, info, warning, error, fatal, or panic + PHOTOPRISM_READONLY: "false" + PHOTOPRISM_EXPERIMENTAL: "false" + PHOTOPRISM_DISABLE_CHOWN: "false" + PHOTOPRISM_DISABLE_WEBDAV: "false" + PHOTOPRISM_DISABLE_SETTINGS: "false" + PHOTOPRISM_DISABLE_TENSORFLOW: "false" + PHOTOPRISM_DISABLE_FACES: "false" + PHOTOPRISM_DISABLE_CLASSIFICATION: "false" + PHOTOPRISM_DISABLE_VECTORS: "false" + PHOTOPRISM_DISABLE_RAW: "false" + PHOTOPRISM_RAW_PRESETS: "false" + PHOTOPRISM_SIDECAR_YAML: "true" + PHOTOPRISM_BACKUP_ALBUMS: "true" + PHOTOPRISM_BACKUP_DATABASE: "true" + PHOTOPRISM_BACKUP_SCHEDULE: "daily" + PHOTOPRISM_INDEX_SCHEDULE: "" + PHOTOPRISM_AUTO_INDEX: 300 + PHOTOPRISM_AUTO_IMPORT: -1 + PHOTOPRISM_DETECT_NSFW: "false" + PHOTOPRISM_UPLOAD_NSFW: "true" + PHOTOPRISM_DATABASE_DRIVER: "mysql" + PHOTOPRISM_DATABASE_SERVER: "mariadb:3306" + PHOTOPRISM_DATABASE_NAME: "photoprism" + PHOTOPRISM_DATABASE_USER: "photoprism" + PHOTOPRISM_DATABASE_PASSWORD: "insecure" + PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App" + PHOTOPRISM_SITE_DESCRIPTION: "" + PHOTOPRISM_SITE_AUTHOR: "" + working_dir: + "/photoprism" + volumes: + - pictures:/photoprism/originals + - storage-data:/photoprism/storage + + mariadb: + image: mariadb:11 + restart: unless-stopped + stop_grace_period: 5s + + security_opt: + - seccomp:unconfined + - apparmor:unconfined + volumes: + - db-data:/var/lib/mysql + environment: + MARIADB_AUTO_UPGRADE: "1" + MARIADB_INITDB_SKIP_TZINFO: "1" + MARIADB_DATABASE: "photoprism" + MARIADB_USER: "photoprism" + MARIADB_PASSWORD: "insecure" + MARIADB_ROOT_PASSWORD: "insecure" + +volumes: + db-data: + storage-data: + pictures: diff --git a/blueprints/picsur/template.toml b/blueprints/picsur/template.toml index 89fcc1431..3b26147a6 100644 --- a/blueprints/picsur/template.toml +++ b/blueprints/picsur/template.toml @@ -5,7 +5,7 @@ admin_password = "${password:32}" jwt_secret = "${jwt:jwt_secret}" [config] - +mounts = [] [[config.domains]] serviceName = "picsur" port = 8080 @@ -16,8 +16,3 @@ host = "${main_domain}" "PICSUR_ADMIN_PASSWORD" = "${admin_password}" "PICSUR_JWT_SECRET" = "${jwt_secret}" "POSTGRES_PASSWORD" = "${postgres_password}" - -[[config.mounts]] -name = "picsur-data" -serviceName = "picsur_postgres" -mountPath = "/var/lib/postgresql/data" diff --git a/blueprints/plark/docker-compose.yml b/blueprints/plark/docker-compose.yml index 133b0bb88..5b8cbf5d3 100644 --- a/blueprints/plark/docker-compose.yml +++ b/blueprints/plark/docker-compose.yml @@ -3,7 +3,7 @@ services: image: plarkinc/plark:latest pull_policy: always restart: unless-stopped - ports: + expose: - "80" volumes: - plark-data:/var/data diff --git a/blueprints/plark/template.toml b/blueprints/plark/template.toml index ce1ccb147..caec9fc71 100644 --- a/blueprints/plark/template.toml +++ b/blueprints/plark/template.toml @@ -2,11 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "plark" port = 80 host = "${main_domain}" [config.env] - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/plunk/docker-compose.yml b/blueprints/plunk/docker-compose.yml index 9991d8554..bf258d094 100644 --- a/blueprints/plunk/docker-compose.yml +++ b/blueprints/plunk/docker-compose.yml @@ -51,7 +51,7 @@ services: MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} volumes: - minio_data:/data - ports: + expose: - 9000 - 9001 healthcheck: @@ -133,13 +133,10 @@ services: # Mount Traefik certificates for SSL - /etc/dokploy/traefik/dynamic/acme.json:/certs/acme.json:ro - ports: - # Main nginx port (handles all subdomain routing) + expose: - 80 - # SMTP ports (for email relay) - 465 - 587 - depends_on: postgres: condition: service_healthy diff --git a/blueprints/pocketbase/template.toml b/blueprints/pocketbase/template.toml index bb43b2137..a429053db 100644 --- a/blueprints/pocketbase/template.toml +++ b/blueprints/pocketbase/template.toml @@ -4,6 +4,7 @@ admin_email = "${email}" admin_password = "${password:32}" [config] +mounts = [] [[config.domains]] serviceName = "pocketbase" port = 8090 @@ -12,7 +13,3 @@ host = "${main_domain}" [config.env] ADMIN_EMAIL = "${admin_email}" ADMIN_PASSWORD = "${admin_password}" - -[[config.mounts]] -name = "pocketbase-data" -mountPath = "/pocketbase" diff --git a/blueprints/portabase/docker-compose.yml b/blueprints/portabase/docker-compose.yml new file mode 100644 index 000000000..59fb0a963 --- /dev/null +++ b/blueprints/portabase/docker-compose.yml @@ -0,0 +1,55 @@ +services: + portabase: + image: portabase/portabase:latest + restart: unless-stopped + environment: + - PROJECT_URL=${PROJECT_URL} + - PROJECT_SECRET=${PROJECT_SECRET} + - DATABASE_URL=${DATABASE_URL} + - PROJECT_NAME=${PROJECT_NAME:-Portabase} + - TZ=${TZ:-Europe/Paris} + - LOG_LEVEL=${LOG_LEVEL:-info} + - RETENTION_CRON=${RETENTION_CRON:-0 7 * * *} + - STALE_BACKUP_THRESHOLD_HOURS=${STALE_BACKUP_THRESHOLD_HOURS:-6} + - BACKUP_FOLDER_NAME=${BACKUP_FOLDER_NAME:-backups} + - TELEMETRY=${TELEMETRY:-true} + - SKIP_ONBOARDING=${SKIP_ONBOARDING:-false} + - API_ENABLED=${API_ENABLED:-false} + - OPENAPI_ENABLED=${OPENAPI_ENABLED:-false} + - MCP_ENABLED=${MCP_ENABLED:-false} + - SMTP_HOST=${SMTP_HOST:-} + - SMTP_PORT=${SMTP_PORT:-} + - SMTP_USER=${SMTP_USER:-} + - SMTP_PASSWORD=${SMTP_PASSWORD:-} + - SMTP_FROM=${SMTP_FROM:-} + - SMTP_SECURE=${SMTP_SECURE:-false} + expose: + - "80" + volumes: + - portabase-data:/data + depends_on: + db: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost/api/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 60s + db: + image: postgres:17-alpine + restart: always + volumes: + - postgres-data:/var/lib/postgresql/data + environment: + - POSTGRES_DB=${POSTGRES_DB:-portabase} + - POSTGRES_USER=${POSTGRES_USER:-portabase} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-portabase} -d ${POSTGRES_DB:-portabase}"] + interval: 10s + timeout: 5s + retries: 5 +volumes: + postgres-data: + portabase-data: diff --git a/blueprints/portabase/meta.json b/blueprints/portabase/meta.json new file mode 100644 index 000000000..360ab0b96 --- /dev/null +++ b/blueprints/portabase/meta.json @@ -0,0 +1,41 @@ +{ + "id": "portabase", + "name": "Portabase", + "version": "latest", + "description": "A free, open-source and self-hosted tool from a non-profit organization that helps you automate backups and restore your databases on demand.", + "logo": "portabase.svg", + "links": { + "github": "https://github.com/portabase/portabase", + "website": "https://portabase.io", + "docs": "https://portabase.io/docs" + }, + "tags": [ + "postgres", + "mysql", + "mariadb", + "mongodb", + "sqlite", + "firebird", + "valkey", + "redis", + "mssql", + "docker-volumes", + "backup", + "s3", + "gcs", + "azure", + "google-drive", + "discord", + "telegram", + "slack", + "email", + "apprise", + "ntfy", + "gotify", + "pushover", + "teams", + "webhook", + "self-hosted", + "open-source" + ] +} diff --git a/blueprints/portabase/portabase.svg b/blueprints/portabase/portabase.svg new file mode 100644 index 000000000..ae94a6686 --- /dev/null +++ b/blueprints/portabase/portabase.svg @@ -0,0 +1,4 @@ + + + + diff --git a/blueprints/portabase/template.toml b/blueprints/portabase/template.toml new file mode 100644 index 000000000..07ecd630a --- /dev/null +++ b/blueprints/portabase/template.toml @@ -0,0 +1,21 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +project_secret = "${password:64}" + +[config] +env = [ + "PROJECT_URL=https://${main_domain}", + "PROJECT_SECRET=${project_secret}", + "POSTGRES_USER=portabase", + "POSTGRES_PASSWORD=${postgres_password}", + "POSTGRES_DB=portabase", + "DATABASE_URL=postgresql://portabase:${postgres_password}@db:5432/portabase?schema=public", + "TZ=Europe/Paris", + "LOG_LEVEL=info", +] + +[[config.domains]] +serviceName = "portabase" +port = 80 +host = "${main_domain}" diff --git a/blueprints/portainer/docker-compose.yml b/blueprints/portainer/docker-compose.yml index 778c3a265..facbf88db 100644 --- a/blueprints/portainer/docker-compose.yml +++ b/blueprints/portainer/docker-compose.yml @@ -5,8 +5,7 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - portainer-data:/data - ports: + expose: - 9000 - volumes: - portainer-data: {} \ No newline at end of file + portainer-data: {} diff --git a/blueprints/portainer/template.toml b/blueprints/portainer/template.toml index 8e1841048..feb0c5a47 100644 --- a/blueprints/portainer/template.toml +++ b/blueprints/portainer/template.toml @@ -2,11 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "portainer" port = 9000 host = "${main_domain}" [config.env] - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/poste.io/docker-compose.yml b/blueprints/poste.io/docker-compose.yml index ad99b8884..9121ea44d 100644 --- a/blueprints/poste.io/docker-compose.yml +++ b/blueprints/poste.io/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — mail protocols (SMTP 25/465/587, POP3 110/995, IMAP 143/993, Sieve 4190) are not HTTP; mail servers and clients must reach them directly on the host. version: "3.8" services: mailserver: diff --git a/blueprints/postgresus/docker-compose.yml b/blueprints/postgresus/docker-compose.yml index ebcdaabbd..0b2299b2c 100644 --- a/blueprints/postgresus/docker-compose.yml +++ b/blueprints/postgresus/docker-compose.yml @@ -3,7 +3,7 @@ services: # Postgresus was renamed upstream to Databasus (https://github.com/databasus/databasus). # This image is frozen at its final release; use the "databasus" template for new deployments. image: rostislavdugin/postgresus:v2.15.3 - ports: + expose: - "4005" volumes: # Persistent data storage diff --git a/blueprints/postiz/docker-compose.yml b/blueprints/postiz/docker-compose.yml index a00832dd2..876bbfb49 100644 --- a/blueprints/postiz/docker-compose.yml +++ b/blueprints/postiz/docker-compose.yml @@ -128,4 +128,4 @@ volumes: postiz-config: postiz-uploads: temporal-es-data: - temporal-pg-data: \ No newline at end of file + temporal-pg-data: diff --git a/blueprints/pre0.22.5-supabase/docker-compose.yml b/blueprints/pre0.22.5-supabase/docker-compose.yml index ee16e34ee..e1934ff89 100644 --- a/blueprints/pre0.22.5-supabase/docker-compose.yml +++ b/blueprints/pre0.22.5-supabase/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-container-names — frozen legacy template (pre-Dokploy 0.22.5). Supabase functionally depends on container names: Kong routes Realtime via its container DNS name and Vector derives log routing from container names. The ${CONTAINER_PREFIX} per-deploy hash prevents name collisions. # Usage # Start: docker compose up # With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up diff --git a/blueprints/privatebin/docker-compose.yml b/blueprints/privatebin/docker-compose.yml index dc621ca0b..ba1757e1a 100644 --- a/blueprints/privatebin/docker-compose.yml +++ b/blueprints/privatebin/docker-compose.yml @@ -4,8 +4,7 @@ services: restart: unless-stopped volumes: - privatebin_data:/srv/data - ports: + expose: - "8080" - volumes: privatebin_data: diff --git a/blueprints/pterodactyl/docker-compose.yml b/blueprints/pterodactyl/docker-compose.yml index 639988182..c75a88a47 100644 --- a/blueprints/pterodactyl/docker-compose.yml +++ b/blueprints/pterodactyl/docker-compose.yml @@ -38,12 +38,6 @@ services: MYSQL_ROOT_PASSWORD: DB_CONNECTION: "mysql" -networks: - default: - ipam: - config: - - subnet: 172.20.0.0/16 - volumes: pterodb: pterovar: diff --git a/blueprints/pulse/docker-compose.yml b/blueprints/pulse/docker-compose.yml index 074aec078..c217209ef 100644 --- a/blueprints/pulse/docker-compose.yml +++ b/blueprints/pulse/docker-compose.yml @@ -19,4 +19,4 @@ services: start_period: 40s volumes: - pulse_data: \ No newline at end of file + pulse_data: diff --git a/blueprints/qbittorrent/docker-compose.yml b/blueprints/qbittorrent/docker-compose.yml index 38e2fdec4..ca019b55f 100644 --- a/blueprints/qbittorrent/docker-compose.yml +++ b/blueprints/qbittorrent/docker-compose.yml @@ -1,5 +1,6 @@ +# dokploy: allow-host-ports — BitTorrent peer connections (6881 TCP/UDP) must reach the host directly; the peering protocol is not HTTP, so Traefik cannot route it. # docker-compose.yml -# +# # IMPORTANT: First-time setup information # - Default username: admin # - Password: Check container logs for temporary password on first startup diff --git a/blueprints/quant-ux/docker-compose.yml b/blueprints/quant-ux/docker-compose.yml index e96a9d6ac..6a1346249 100644 --- a/blueprints/quant-ux/docker-compose.yml +++ b/blueprints/quant-ux/docker-compose.yml @@ -19,7 +19,7 @@ services: links: - mongo - qux-be - ports: + expose: - 8082 depends_on: - qux-be @@ -56,7 +56,7 @@ services: environment: - QUX_SERVER=http://quant-ux-backend:8080/ - QUX_SERVER_PORT=8086 - ports: + expose: - 8086 links: - qux-be diff --git a/blueprints/rabbitmq/docker-compose.yml b/blueprints/rabbitmq/docker-compose.yml index 3919a98f7..c8d54f970 100644 --- a/blueprints/rabbitmq/docker-compose.yml +++ b/blueprints/rabbitmq/docker-compose.yml @@ -9,9 +9,8 @@ services: - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbit log_levels [{connection,error},{default,error}] disk_free_limit ${RABBITMQ_DISK_FREE_LIMIT} volumes: - rabbitmq-data:/var/lib/rabbitmq - ports: + expose: - 15672 - 5672 - volumes: - rabbitmq-data: {} \ No newline at end of file + rabbitmq-data: {} diff --git a/blueprints/rallly/docker-compose.yml b/blueprints/rallly/docker-compose.yml index ac6badae1..aeed3b1d3 100644 --- a/blueprints/rallly/docker-compose.yml +++ b/blueprints/rallly/docker-compose.yml @@ -63,4 +63,4 @@ services: volumes: rallly-db: - rallly-minio: \ No newline at end of file + rallly-minio: diff --git a/blueprints/reef-dev-cluster/docker-compose.yml b/blueprints/reef-dev-cluster/docker-compose.yml new file mode 100644 index 000000000..49b726af8 --- /dev/null +++ b/blueprints/reef-dev-cluster/docker-compose.yml @@ -0,0 +1,129 @@ +services: + reef-dev-cluster: + image: anukulpandey/reef-chain-node:latest + restart: unless-stopped + working_dir: /workspace + + environment: + v1sec: "${v1sec:-}" + v2sec: "${v2sec:-}" + v3sec: "${v3sec:-}" + faucetsec: "${faucetsec:-}" + DEFAULT_AMOUNT: "${DEFAULT_AMOUNT:-2000}" + MAX_AMOUNT: "${MAX_AMOUNT:-2000}" + + volumes: + - reef-dev-cluster-state:/workspace/state + - reef-dev-cluster-download:/workspace/download + + expose: + - "30335" + - "30333" + - "30334" + - "30336" + - "8001" + - "9944" + - "9945" + - "8080" + + healthcheck: + test: + - CMD-SHELL + - bash -lc 'exec 3<>/dev/tcp/127.0.0.1/9944' + interval: 10s + timeout: 5s + retries: 30 + start_period: 300s + + entrypoint: /bin/sh + + command: + - -c + - | + set -e + + export DEBIAN_FRONTEND=noninteractive + + echo '📦 Installing required tools...' + + apt-get update + + apt-get install -y \ + wget \ + curl \ + python3 \ + ca-certificates \ + gnupg \ + nginx + + mkdir -p \ + /etc/apt/keyrings \ + /workspace \ + /workspace/state \ + /workspace/download + + echo '🔑 Configuring NodeSource repository...' + + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ + | gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg + + echo 'deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main' \ + > /etc/apt/sources.list.d/nodesource.list + + apt-get update + + apt-get install -y nodejs + + echo '📥 Downloading cluster bootstrap...' + + wget -O /workspace/run-cluster.sh \ + https://raw.githubusercontent.com/anukulpandey/dokploy-templates/d8a82420dad013a3eb7e5d8afc0b9f2700a0045a/blueprints/reef-dev-cluster/run-cluster.sh + + echo '🔧 Making script executable...' + + chmod +x /workspace/run-cluster.sh + + echo '🚀 Starting self-contained network...' + + exec env TEMPLATE_ASSET_REF=d8a82420dad013a3eb7e5d8afc0b9f2700a0045a \ + /workspace/run-cluster.sh + + reef-dev-cluster-eth-rpc: + image: anukulpandey/reef-chain-eth-rpc:latest + restart: unless-stopped + working_dir: /workspace + + depends_on: + reef-dev-cluster: + condition: service_healthy + + expose: + - "8545" + + environment: + NODE_RPC_URL: ws://reef-dev-cluster:9945 + PORT: "8545" + + healthcheck: + test: + - CMD-SHELL + - bash -lc 'exec 3<>/dev/tcp/127.0.0.1/8545' + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s + + entrypoint: /bin/sh + + command: + - -c + - | + exec eth-rpc \ + --node-rpc-url "$$NODE_RPC_URL" \ + --rpc-port "$$PORT" \ + --rpc-external \ + --rpc-cors all + +volumes: + reef-dev-cluster-state: + reef-dev-cluster-download: diff --git a/blueprints/reef-dev-cluster/faucet/package.json b/blueprints/reef-dev-cluster/faucet/package.json new file mode 100644 index 000000000..fd45ff3d7 --- /dev/null +++ b/blueprints/reef-dev-cluster/faucet/package.json @@ -0,0 +1,13 @@ +{ + "name": "reef-dev-cluster-faucet", + "version": "1.0.0", + "private": true, + "description": "HTTP faucet for a self-contained Reef dev cluster", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "@polkadot/api": "^16.4.8" + } +} diff --git a/blueprints/reef-dev-cluster/faucet/server.js b/blueprints/reef-dev-cluster/faucet/server.js new file mode 100644 index 000000000..15d1ce32b --- /dev/null +++ b/blueprints/reef-dev-cluster/faucet/server.js @@ -0,0 +1,696 @@ +#!/usr/bin/env node + +const http = require("node:http"); +const { ApiPromise, Keyring, WsProvider } = require("@polkadot/api"); + +const config = { + wsEndpoint: process.env.WS_ENDPOINT || "ws://127.0.0.1:9944", + evmRpcUrl: process.env.EVM_RPC_URL || "", + faucetSeed: process.env.FAUCET_SEED || "", + port: Number.parseInt(process.env.PORT || "8080", 10), + defaultAmount: String(process.env.DEFAULT_AMOUNT || "2000"), + maxAmount: String(process.env.MAX_AMOUNT || "2000"), + serviceName: process.env.SERVICE_NAME || "reef-faucet", +}; + +if (!config.faucetSeed) { + throw new Error("FAUCET_SEED is required"); +} + +let contextPromise = null; +let requestQueue = Promise.resolve(); + +function normalizeEvmAddress(value) { + const normalized = String(value || "").trim(); + if (!/^0x[0-9a-fA-F]{40}$/.test(normalized)) { + throw new Error(`Invalid EVM address: ${value}`); + } + return normalized; +} + +function parseUnits(value, decimals) { + const input = String(value).trim(); + if (!/^\d+(\.\d+)?$/.test(input)) { + throw new Error(`Invalid decimal amount: ${value}`); + } + + const [whole, fraction = ""] = input.split("."); + if (fraction.length > decimals) { + throw new Error(`Too many decimal places in ${value}`); + } + + const wholeUnits = BigInt(whole) * 10n ** BigInt(decimals); + const fractionUnits = fraction ? BigInt(fraction.padEnd(decimals, "0")) : 0n; + return wholeUnits + fractionUnits; +} + +function formatUnits(value, decimals) { + const negative = value < 0n; + const abs = negative ? -value : value; + const base = 10n ** BigInt(decimals); + const whole = abs / base; + const fraction = abs % base; + + if (fraction === 0n) { + return `${negative ? "-" : ""}${whole.toString()}`; + } + + return `${negative ? "-" : ""}${whole.toString()}.${fraction + .toString() + .padStart(decimals, "0") + .replace(/0+$/, "")}`; +} + +function fallbackAccountFromEvm(evmAddress) { + return `0x${evmAddress.slice(2).toLowerCase()}${"ee".repeat(12)}`; +} + +function decodeDispatchError(api, dispatchError) { + if (dispatchError && dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + return `${decoded.section}.${decoded.name}: ${decoded.docs.join(" ")}`; + } + + return dispatchError && dispatchError.toString ? dispatchError.toString() : String(dispatchError); +} + +async function signAndWait(api, tx, signer) { + return new Promise(async (resolve, reject) => { + let unsubscribe = null; + + try { + unsubscribe = await tx.signAndSend(signer, ({ dispatchError, events, status, txHash }) => { + if (dispatchError) { + if (unsubscribe) { + unsubscribe(); + } + reject(new Error(decodeDispatchError(api, dispatchError))); + return; + } + + if (status.isFinalized) { + if (unsubscribe) { + unsubscribe(); + } + resolve({ + txHash: txHash.toHex(), + finalizedBlock: status.asFinalized.toHex(), + eventCount: events.length, + }); + } + }); + } catch (error) { + if (unsubscribe) { + unsubscribe(); + } + reject(error); + } + }); +} + +async function maybeClaimDefaultAccount(api, signer) { + const storage = api.query && api.query.evmAccounts && api.query.evmAccounts.evmAddresses; + const extrinsic = api.tx && api.tx.evmAccounts && api.tx.evmAccounts.claimDefaultAccount; + + if (!storage || !extrinsic) { + return null; + } + + const current = await storage(signer.address); + if (!current.isEmpty) { + return current.toString(); + } + + await signAndWait(api, extrinsic(), signer); + return (await storage(signer.address)).toString(); +} + +function buildReviveTransfer(api, signerAddress, targetEvm, amountUnits) { + const reviveTransfer = api.tx && api.tx.revive && api.tx.revive.transfer; + if (!reviveTransfer) { + return null; + } + + const metaArgs = reviveTransfer.meta.toJSON().args || []; + const names = metaArgs.map((arg) => String((arg && arg.name) || "").toLowerCase()); + const types = metaArgs.map((arg) => JSON.stringify((arg && arg.type) || "").toLowerCase()); + const haystack = `${names.join(",")}|${types.join(",")}`; + + if (metaArgs.length === 2 && haystack.includes("h160")) { + return { + strategy: "revive.transfer(H160, Balance)", + tx: reviveTransfer(targetEvm, amountUnits), + }; + } + + if (metaArgs.length === 3 && haystack.includes("accountid") && haystack.includes("h160")) { + return { + strategy: "revive.transfer(AccountId, H160, Balance)", + tx: reviveTransfer(signerAddress, targetEvm, amountUnits), + }; + } + + return { + strategy: "revive.transfer(fallback call signature)", + tx: reviveTransfer(targetEvm, amountUnits), + }; +} + +async function getContext() { + if (!contextPromise) { + contextPromise = (async () => { + const provider = new WsProvider(config.wsEndpoint); + const api = await ApiPromise.create({ provider }); + const keyring = new Keyring({ type: "sr25519" }); + const sender = keyring.addFromUri(config.faucetSeed); + const nativeDecimals = api.registry.chainDecimals[0] || 12; + const tokenSymbol = api.registry.chainTokens[0] || "REEF"; + + return { api, nativeDecimals, provider, sender, tokenSymbol }; + })(); + } + + return contextPromise; +} + +function jsonResponse(response, statusCode, body) { + response.writeHead(statusCode, { "Content-Type": "application/json" }); + response.end(JSON.stringify(body)); +} + +function htmlResponse(response, statusCode, body) { + response.writeHead(statusCode, { "Content-Type": "text/html; charset=utf-8" }); + response.end(body); +} + +function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +function wantsHtml(request) { + const accept = String(request.headers.accept || ""); + return accept.includes("text/html"); +} + +function buildInfoPayload() { + return { + service: config.serviceName, + status: "ok", + defaults: { + defaultAmount: config.defaultAmount, + maxAmount: config.maxAmount, + }, + upstream: { + wsEndpoint: config.wsEndpoint, + evmRpcUrl: config.evmRpcUrl || null, + }, + endpoints: { + health: { + method: "GET", + path: "/health", + }, + drip: { + method: "POST", + path: "/drip", + body: { + to: "0x0000000000000000000000000000000000000000", + amount: config.defaultAmount, + }, + }, + }, + }; +} + +function buildFrontendHtml() { + const bootPayload = JSON.stringify(buildInfoPayload()); + const title = escapeHtml(config.serviceName); + const defaultAmount = escapeHtml(config.defaultAmount); + const maxAmount = escapeHtml(config.maxAmount); + const wsEndpoint = escapeHtml(config.wsEndpoint); + const evmRpcUrl = escapeHtml(config.evmRpcUrl || "Not configured"); + + return ` + + + + + ${title} + + + +
+
+
+

Reef Faucet

+

${title}

+

Send test REEF to any EVM address without reaching for curl first.

+
+ +
+
+ Default amount + ${defaultAmount} REEF +
+
+ Max amount + ${maxAmount} REEF +
+
+ WS upstream + ${wsEndpoint} +
+
+ EVM upstream + ${evmRpcUrl} +
+
+ +
+ + + + +
+ + +
+
+ +
+
Ready.
+
+ + +
+
+ + + +`; +} + +async function parseRequestBody(request) { + const chunks = []; + for await (const chunk of request) { + chunks.push(chunk); + } + + if (chunks.length === 0) { + return {}; + } + + return JSON.parse(Buffer.concat(chunks).toString("utf8")); +} + +async function executeDrip(targetEvm, amountText) { + const { api, nativeDecimals, sender, tokenSymbol } = await getContext(); + const amountUnits = parseUnits(amountText, nativeDecimals); + const maxUnits = parseUnits(config.maxAmount, nativeDecimals); + + if (amountUnits <= 0n) { + throw new Error("Amount must be greater than zero"); + } + + if (amountUnits > maxUnits) { + throw new Error(`Amount exceeds MAX_AMOUNT (${config.maxAmount})`); + } + + await maybeClaimDefaultAccount(api, sender); + + const senderBefore = (await api.query.system.account(sender.address)).data.free.toBigInt(); + + let strategy = ""; + let result = null; + + try { + const revive = buildReviveTransfer(api, sender.address, targetEvm, amountUnits); + if (!revive) { + throw new Error("revive.transfer unavailable"); + } + strategy = revive.strategy; + result = await signAndWait(api, revive.tx, sender); + } catch (error) { + strategy = "balances.transferAllowDeath(AccountId32 fallback)"; + result = await signAndWait( + api, + api.tx.balances.transferAllowDeath(fallbackAccountFromEvm(targetEvm), amountUnits), + sender + ); + } + + const senderAfter = (await api.query.system.account(sender.address)).data.free.toBigInt(); + + return { + amount: amountText, + strategy, + tokenSymbol, + txHash: result.txHash, + finalizedBlock: result.finalizedBlock, + eventCount: result.eventCount, + sender: sender.address, + senderNativeBefore: formatUnits(senderBefore, nativeDecimals), + senderNativeAfter: formatUnits(senderAfter, nativeDecimals), + }; +} + +function enqueue(task) { + const current = requestQueue.then(task, task); + requestQueue = current.catch(() => undefined); + return current; +} + +const server = http.createServer(async (request, response) => { + try { + if (request.method === "GET" && request.url === "/favicon.ico") { + response.writeHead(204); + response.end(); + return; + } + + if (request.method === "GET" && request.url === "/") { + if (wantsHtml(request)) { + htmlResponse(response, 200, buildFrontendHtml()); + } else { + jsonResponse(response, 200, buildInfoPayload()); + } + return; + } + + if (request.method === "GET" && request.url === "/health") { + jsonResponse(response, 200, { status: "ok" }); + return; + } + + if (request.method === "POST" && request.url === "/drip") { + const payload = await parseRequestBody(request); + const targetEvm = normalizeEvmAddress(payload.to); + const amount = payload.amount ? String(payload.amount) : config.defaultAmount; + const result = await enqueue(() => executeDrip(targetEvm, amount)); + jsonResponse(response, 200, result); + return; + } + + jsonResponse(response, 404, { error: "Not found" }); + } catch (error) { + jsonResponse(response, 400, { error: error.message }); + } +}); + +server.listen(config.port, "0.0.0.0", () => { + console.log(`reef dev-cluster faucet listening on ${config.port}`); +}); diff --git a/blueprints/reef-dev-cluster/meta.json b/blueprints/reef-dev-cluster/meta.json new file mode 100644 index 000000000..05687870b --- /dev/null +++ b/blueprints/reef-dev-cluster/meta.json @@ -0,0 +1,19 @@ +{ + "id": "reef-dev-cluster", + "name": "Reef Chain - Dev Cluster", + "version": "1", + "description": "Self-contained Reef dev cluster with bootnode, validators, spec server, substrate RPC, EVM RPC, and faucet", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "dev-cluster", + "self-hosted", + "evm", + "faucet" + ] +} diff --git a/blueprints/reef-dev-cluster/reef.svg b/blueprints/reef-dev-cluster/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-dev-cluster/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-dev-cluster/run-cluster.sh b/blueprints/reef-dev-cluster/run-cluster.sh new file mode 100755 index 000000000..4dca6dc68 --- /dev/null +++ b/blueprints/reef-dev-cluster/run-cluster.sh @@ -0,0 +1,549 @@ +#!/usr/bin/env bash + +set -euo pipefail + +NODE_BIN="${NODE_BIN:-reef-node}" +CHAIN_TEMPLATE="${CHAIN_TEMPLATE:-testnet-new}" +WORK_DIR="${WORK_DIR:-/tmp/reef-dev-cluster}" +STATE_DIR="${STATE_DIR:-/workspace/state}" +SEED_DIR="${SEED_DIR:-$STATE_DIR/seeds}" +NODE_KEY_DIR="${NODE_KEY_DIR:-$STATE_DIR/node-keys}" +OUTPUT_DIR="${OUTPUT_DIR:-/workspace/download}" +PLAIN_SPEC="${PLAIN_SPEC:-$WORK_DIR/local-chain-spec.json}" +UPDATED_SPEC="${UPDATED_SPEC:-$WORK_DIR/local-chain-spec-updated.json}" +SPEC_FILE="${SPEC_FILE:-$OUTPUT_DIR/local-chain-spec-raw.json}" +SPEC_HTTP_PORT="${SPEC_HTTP_PORT:-8001}" +BOOTNODE_P2P_PORT="${BOOTNODE_P2P_PORT:-30335}" +VALIDATOR1_P2P_PORT="${VALIDATOR1_P2P_PORT:-30333}" +VALIDATOR2_P2P_PORT="${VALIDATOR2_P2P_PORT:-30334}" +VALIDATOR3_P2P_PORT="${VALIDATOR3_P2P_PORT:-30336}" +RPC_NODE_P2P_PORT="${RPC_NODE_P2P_PORT:-30337}" +RPC_NODE_WS_PORT="${RPC_NODE_WS_PORT:-9944}" +RPC_NODE_PUBLIC_WS_PORT="${RPC_NODE_PUBLIC_WS_PORT:-9945}" +ETH_RPC_PORT="${ETH_RPC_PORT:-8545}" +FAUCET_PORT="${FAUCET_PORT:-8080}" +BOOTNODE_PROMETHEUS_PORT="${BOOTNODE_PROMETHEUS_PORT:-9615}" +VALIDATOR1_PROMETHEUS_PORT="${VALIDATOR1_PROMETHEUS_PORT:-9616}" +VALIDATOR2_PROMETHEUS_PORT="${VALIDATOR2_PROMETHEUS_PORT:-9617}" +VALIDATOR3_PROMETHEUS_PORT="${VALIDATOR3_PROMETHEUS_PORT:-9618}" +RPC_NODE_PROMETHEUS_PORT="${RPC_NODE_PROMETHEUS_PORT:-9619}" +DEFAULT_AMOUNT="${DEFAULT_AMOUNT:-2000}" +MAX_AMOUNT="${MAX_AMOUNT:-2000}" +TEMPLATE_ASSET_REF="${TEMPLATE_ASSET_REF:-reef-chain}" +TEMPLATE_ASSET_BASE_URL="${TEMPLATE_ASSET_BASE_URL:-https://raw.githubusercontent.com/anukulpandey/dokploy-templates/${TEMPLATE_ASSET_REF}/blueprints/reef-dev-cluster}" +FAUCET_DIR="${FAUCET_DIR:-/workspace/faucet}" +BOOTNODE_PEER_ID_FILE="$OUTPUT_DIR/bootnode_peer_id.txt" +BOOTNODE_COMPAT_FILE="$OUTPUT_DIR/bootnode_node_key.txt" +CLUSTER_INFO_FILE="$OUTPUT_DIR/cluster-info.json" + +PIDS=() +TAIL_PID="" + +cleanup() { + kill "$TAIL_PID" 2>/dev/null || true + if [ "${#PIDS[@]}" -gt 0 ]; then + kill "${PIDS[@]}" 2>/dev/null || true + fi +} + +trap cleanup EXIT + +trim_whitespace() { + printf '%s' "$1" | tr -d '[:space:]' +} + +canonicalize_seed() { + local name="$1" + local value + value=$(trim_whitespace "${2:-}") + + if [ -z "$value" ]; then + printf '\n' + return 0 + fi + + if [[ "$value" =~ ^0x[0-9A-Fa-f]{64}$ ]]; then + printf '0x%s\n' "$(printf '%s' "${value#0x}" | tr '[:upper:]' '[:lower:]')" + return 0 + fi + + if [[ "$value" =~ ^[0-9A-Fa-f]{64}$ ]]; then + printf '0x%s\n' "$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')" + return 0 + fi + + echo "Invalid seed for $name. Expected 64 hex bytes with optional 0x prefix." >&2 + exit 1 +} + +write_secret_file() { + local file="$1" + local value="$2" + + mkdir -p "$(dirname "$file")" + printf '%s\n' "$value" > "$file" + chmod 600 "$file" +} + +generate_seed() { + printf '0x%s\n' "$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n' | tr '[:upper:]' '[:lower:]')" +} + +resolve_seed() { + local env_name="$1" + local file_name="$2" + local file="$SEED_DIR/$file_name" + local raw_value normalized source + + raw_value=$(trim_whitespace "${!env_name:-}") + if [ -n "$raw_value" ]; then + normalized=$(canonicalize_seed "$env_name" "$raw_value") + source="env" + write_secret_file "$file" "$normalized" + elif [ -f "$file" ]; then + normalized=$(canonicalize_seed "$env_name" "$(cat "$file")") + source="persisted" + write_secret_file "$file" "$normalized" + else + normalized=$(generate_seed) + source="generated" + write_secret_file "$file" "$normalized" + fi + + printf '%s|%s\n' "$normalized" "$source" +} + +resolve_optional_seed() { + local env_name="$1" + local file_name="$2" + local file="$SEED_DIR/$file_name" + local raw_value normalized source + + raw_value=$(trim_whitespace "${!env_name:-}") + if [ -n "$raw_value" ]; then + normalized=$(canonicalize_seed "$env_name" "$raw_value") + source="env" + write_secret_file "$file" "$normalized" + elif [ -f "$file" ]; then + normalized=$(canonicalize_seed "$env_name" "$(cat "$file")") + source="persisted" + write_secret_file "$file" "$normalized" + else + normalized="" + source="shared-v1" + fi + + printf '%s|%s\n' "$normalized" "$source" +} + +read_seed_result() { + local var_prefix="$1" + local result="$2" + local seed="${result%%|*}" + local source="${result##*|}" + + printf -v "${var_prefix}_SEED" '%s' "$seed" + printf -v "${var_prefix}_SOURCE" '%s' "$source" +} + +ensure_node_key_file() { + local name="$1" + local file="$NODE_KEY_DIR/${name}.key" + + if [ ! -s "$file" ]; then + mkdir -p "$NODE_KEY_DIR" + "$NODE_BIN" key generate-node-key --chain local > "$file" + chmod 600 "$file" + fi + + printf '%s\n' "$file" +} + +derive_address() { + local suri="$1" + local scheme="${2:-Sr25519}" + + "$NODE_BIN" key inspect --scheme "$scheme" "$suri" --output-type json \ + | grep -o '"ss58Address": "[^"]*"' \ + | cut -d'"' -f4 +} + +insert_keys() { + local base_path="$1" + local seed="$2" + + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Sr25519 --suri "$seed//babe" --key-type babe + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Ed25519 --suri "$seed//grandpa" --key-type gran + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Sr25519 --suri "$seed//im_online" --key-type imon + "$NODE_BIN" key insert --base-path "$base_path" --chain "$SPEC_FILE" --scheme Sr25519 --suri "$seed//authority_discovery" --key-type audi +} + +start_logged() { + local log_file="$1" + shift + + "$@" >"$log_file" 2>&1 & + PIDS+=("$!") +} + +write_ws_proxy_config() { + cat > /tmp/rpc-nginx.conf </dev/null || true + +echo "Resolved validator addresses:" +echo " - Validator1: $V1_ADDR ($V1_SOURCE)" +echo " - Validator2: $V2_ADDR ($V2_SOURCE)" +echo " - Validator3: $V3_ADDR ($V3_SOURCE)" +echo " - Faucet: $FAUCET_ADDR ($FAUCET_SOURCE)" +echo "Private state directory: $STATE_DIR" + +echo "Downloading local faucet sources..." +wget -q -O "$FAUCET_DIR/package.json" \ + "${TEMPLATE_ASSET_BASE_URL}/faucet/package.json" +wget -q -O "$FAUCET_DIR/server.js" \ + "${TEMPLATE_ASSET_BASE_URL}/faucet/server.js" + +echo "Installing faucet dependencies..." +( + cd "$FAUCET_DIR" + npm install --omit=dev --no-fund --no-audit +) + +echo "Generating chain spec..." +"$NODE_BIN" build-spec --chain "$CHAIN_TEMPLATE" --disable-default-bootnode > "$PLAIN_SPEC" + +python3 - "$PLAIN_SPEC" "$UPDATED_SPEC" \ + "$V1_ADDR" "$V1_BABE" "$V1_GRAN" "$V1_IMON" "$V1_AUDI" \ + "$V2_ADDR" "$V2_BABE" "$V2_GRAN" "$V2_IMON" "$V2_AUDI" \ + "$V3_ADDR" "$V3_BABE" "$V3_GRAN" "$V3_IMON" "$V3_AUDI" \ + "$FAUCET_ADDR" "$V1_ADDR" <<'PY' +import json +import sys + +input_file = sys.argv[1] +output_file = sys.argv[2] + +v1_addr, v1_babe, v1_gran, v1_imon, v1_audi = sys.argv[3:8] +v2_addr, v2_babe, v2_gran, v2_imon, v2_audi = sys.argv[8:13] +v3_addr, v3_babe, v3_gran, v3_imon, v3_audi = sys.argv[13:18] +faucet_addr = sys.argv[18] +default_faucet_addr = sys.argv[19] + +AMOUNT = 100000000000000000000000000 +STAKE = 1000000000000000000000000 + +with open(input_file, "r", encoding="utf-8") as handle: + spec = json.load(handle) + +balances = spec["genesis"]["runtimeGenesis"]["patch"]["balances"]["balances"] + +def upsert_balance(address, amount): + for entry in balances: + if entry[0] == address: + entry[1] = max(int(entry[1]), amount) + return + balances.append([address, amount]) + +for address in (v1_addr, v2_addr, v3_addr): + upsert_balance(address, AMOUNT) + +if faucet_addr != default_faucet_addr: + upsert_balance(faucet_addr, AMOUNT) + +spec["genesis"]["runtimeGenesis"]["patch"]["session"]["keys"] = [ + [ + v1_addr, + v1_addr, + { + "authority_discovery": v1_audi, + "babe": v1_babe, + "grandpa": v1_gran, + "im_online": v1_imon, + }, + ], + [ + v2_addr, + v2_addr, + { + "authority_discovery": v2_audi, + "babe": v2_babe, + "grandpa": v2_gran, + "im_online": v2_imon, + }, + ], + [ + v3_addr, + v3_addr, + { + "authority_discovery": v3_audi, + "babe": v3_babe, + "grandpa": v3_gran, + "im_online": v3_imon, + }, + ], +] + +spec["genesis"]["runtimeGenesis"]["patch"]["staking"]["invulnerables"] = [ + v1_addr, + v2_addr, + v3_addr, +] + +spec["genesis"]["runtimeGenesis"]["patch"]["staking"]["stakers"] = [ + [v1_addr, v1_addr, STAKE, "Validator"], + [v2_addr, v2_addr, STAKE, "Validator"], + [v3_addr, v3_addr, STAKE, "Validator"], +] + +with open(output_file, "w", encoding="utf-8") as handle: + json.dump(spec, handle, indent=2) +PY + +"$NODE_BIN" build-spec \ + --chain "$UPDATED_SPEC" \ + --disable-default-bootnode \ + --raw > "$SPEC_FILE" + +BOOTNODE_NODE_KEY_FILE=$(ensure_node_key_file bootnode) +V1_NODE_KEY_FILE=$(ensure_node_key_file validator1) +V2_NODE_KEY_FILE=$(ensure_node_key_file validator2) +V3_NODE_KEY_FILE=$(ensure_node_key_file validator3) +RPC_NODE_KEY_FILE=$(ensure_node_key_file rpc-node) + +BOOTNODE_PEER_ID=$("$NODE_BIN" key inspect-node-key --file "$BOOTNODE_NODE_KEY_FILE" 2>/dev/null | tail -n1 | tr -d '\r') +if [ -z "$BOOTNODE_PEER_ID" ]; then + echo "Failed to derive bootnode peer id" >&2 + exit 1 +fi + +printf '%s\n' "$BOOTNODE_PEER_ID" > "$BOOTNODE_PEER_ID_FILE" +printf '%s\n' "$BOOTNODE_PEER_ID" > "$BOOTNODE_COMPAT_FILE" +write_cluster_info + +echo "Bootnode peer ID: $BOOTNODE_PEER_ID" + +insert_keys /tmp/validator1 "$V1_SEED" +insert_keys /tmp/validator2 "$V2_SEED" +insert_keys /tmp/validator3 "$V3_SEED" + +python3 -m http.server "$SPEC_HTTP_PORT" --bind 0.0.0.0 --directory "$OUTPUT_DIR" >/tmp/spec-server.log 2>&1 & +PIDS+=("$!") + +BOOTNODE_MULTIADDR="/ip4/127.0.0.1/tcp/${BOOTNODE_P2P_PORT}/p2p/${BOOTNODE_PEER_ID}" + +write_ws_proxy_config + +start_logged /tmp/bootnode.log \ + "$NODE_BIN" \ + --base-path /tmp/bootnode \ + --chain "$SPEC_FILE" \ + --port "$BOOTNODE_P2P_PORT" \ + --prometheus-port "$BOOTNODE_PROMETHEUS_PORT" \ + --rpc-port 0 \ + --node-key-file "$BOOTNODE_NODE_KEY_FILE" \ + --name Bootnode \ + --no-telemetry + +start_logged /tmp/validator1.log \ + "$NODE_BIN" \ + --base-path /tmp/validator1 \ + --chain "$SPEC_FILE" \ + --port "$VALIDATOR1_P2P_PORT" \ + --prometheus-port "$VALIDATOR1_PROMETHEUS_PORT" \ + --rpc-port 0 \ + --node-key-file "$V1_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --validator \ + --name Validator1 \ + --no-telemetry + +start_logged /tmp/validator2.log \ + "$NODE_BIN" \ + --base-path /tmp/validator2 \ + --chain "$SPEC_FILE" \ + --port "$VALIDATOR2_P2P_PORT" \ + --prometheus-port "$VALIDATOR2_PROMETHEUS_PORT" \ + --rpc-port 0 \ + --node-key-file "$V2_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --validator \ + --name Validator2 \ + --no-telemetry + +start_logged /tmp/validator3.log \ + "$NODE_BIN" \ + --base-path /tmp/validator3 \ + --chain "$SPEC_FILE" \ + --port "$VALIDATOR3_P2P_PORT" \ + --prometheus-port "$VALIDATOR3_PROMETHEUS_PORT" \ + --rpc-port 0 \ + --node-key-file "$V3_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --validator \ + --name Validator3 \ + --no-telemetry + +start_logged /tmp/rpc-node.log \ + "$NODE_BIN" \ + --base-path /tmp/rpc-node \ + --chain "$SPEC_FILE" \ + --port "$RPC_NODE_P2P_PORT" \ + --prometheus-port "$RPC_NODE_PROMETHEUS_PORT" \ + --node-key-file "$RPC_NODE_KEY_FILE" \ + --bootnodes "$BOOTNODE_MULTIADDR" \ + --rpc-external \ + --rpc-port "$RPC_NODE_WS_PORT" \ + --rpc-cors all \ + --rpc-methods Unsafe \ + --rpc-max-connections 10000 \ + --pruning archive \ + --name rpc-node \ + --no-telemetry + +start_logged /tmp/ws-proxy.log \ + nginx \ + -c /tmp/rpc-nginx.conf \ + -g 'daemon off;' + +start_logged /tmp/faucet.log \ + env \ + SERVICE_NAME="reef-dev-cluster-faucet" \ + PORT="$FAUCET_PORT" \ + WS_ENDPOINT="ws://127.0.0.1:${RPC_NODE_WS_PORT}" \ + EVM_RPC_URL="http://127.0.0.1:${ETH_RPC_PORT}" \ + FAUCET_SEED="$FAUCET_SEED" \ + DEFAULT_AMOUNT="$DEFAULT_AMOUNT" \ + MAX_AMOUNT="$MAX_AMOUNT" \ + npm --prefix "$FAUCET_DIR" start + +tail -F /tmp/spec-server.log /tmp/bootnode.log /tmp/validator1.log /tmp/validator2.log /tmp/validator3.log /tmp/rpc-node.log /tmp/ws-proxy.log /tmp/faucet.log & +TAIL_PID=$! + +wait -n "${PIDS[@]}" +STATUS=$? +exit "$STATUS" diff --git a/blueprints/reef-dev-cluster/template.toml b/blueprints/reef-dev-cluster/template.toml new file mode 100644 index 000000000..05345c2bd --- /dev/null +++ b/blueprints/reef-dev-cluster/template.toml @@ -0,0 +1,51 @@ +[variables] +# Optional advanced overrides. Leave blank to auto-generate and persist validator identities. +v1sec = "" +v2sec = "" +v3sec = "" + +# Optional dedicated faucet seed. Leave blank to reuse validator 1. +faucetsec = "" +DEFAULT_AMOUNT = "2000" +MAX_AMOUNT = "2000" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8001 +host = "${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8001 +host = "spec.${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 9945 +host = "ws.${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster-eth-rpc" +port = 8545 +host = "eth.${domain}" +path = "/" + +[[config.domains]] +serviceName = "reef-dev-cluster" +port = 8080 +host = "faucet.${domain}" +path = "/" + +[config.env] +# Optional advanced overrides. Leave blank to auto-generate and persist validator identities. +v1sec = "" +v2sec = "" +v3sec = "" + +# Optional dedicated faucet seed. Leave blank to reuse validator 1. +faucetsec = "" +DEFAULT_AMOUNT = "2000" +MAX_AMOUNT = "2000" diff --git a/blueprints/reef-keygen/docker-compose.yml b/blueprints/reef-keygen/docker-compose.yml new file mode 100644 index 000000000..b6d332ce2 --- /dev/null +++ b/blueprints/reef-keygen/docker-compose.yml @@ -0,0 +1,41 @@ +version: "3.8" + +services: + reef-keygen: + image: anukulpandey/reef-chain-node:latest + restart: unless-stopped + working_dir: /workspace + volumes: + - ./output:/output + expose: + - "${PORT}" + entrypoint: /bin/sh + command: -c " + set -e && + + echo '📦 Installing python3...' && + apt-get update && + apt-get install -y python3 && + + mkdir -p /output && + chmod -R 777 /output && + + echo '=====================================' && + echo '🔑 Generating Validator Keys' && + echo '=====================================' && + + reef-node key generate --scheme Sr25519 --output-type json > /output/validator1.json && + reef-node key generate-node-key --chain local > /output/v1_node_key.txt && + + reef-node key generate --scheme Sr25519 --output-type json > /output/validator2.json && + reef-node key generate-node-key --chain local > /output/v2_node_key.txt && + + reef-node key generate --scheme Sr25519 --output-type json > /output/validator3.json && + reef-node key generate-node-key --chain local > /output/v3_node_key.txt && + + echo '✅ Keys generated:' && + ls -lah /output && + + echo '🌐 Starting HTTP server on port ${PORT}' && + cd /output && + python3 -m http.server ${PORT} --bind 0.0.0.0" diff --git a/blueprints/reef-keygen/meta.json b/blueprints/reef-keygen/meta.json new file mode 100644 index 000000000..9f1d7a4bd --- /dev/null +++ b/blueprints/reef-keygen/meta.json @@ -0,0 +1,18 @@ +{ + "id": "reef-keygen", + "name": "Reef Chain - Keys Generator", + "version": "1", + "description": "Generates 3 Validators keys for a custom Reef Chain cluster", + "links": { + "github": "https://github.com/reef-chain/chain-upgrade", + "website": "https://hub.docker.com/repository/docker/anukulpandey/reef-chain-node", + "docs": "https://docs.reef.io/" + }, + "logo": "reef.svg", + "tags": [ + "reef-chain", + "keys generator", + "self-hosted", + "keys" + ] +} diff --git a/blueprints/reef-keygen/reef.svg b/blueprints/reef-keygen/reef.svg new file mode 100644 index 000000000..598d7930a --- /dev/null +++ b/blueprints/reef-keygen/reef.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/blueprints/reef-keygen/template.toml b/blueprints/reef-keygen/template.toml new file mode 100644 index 000000000..6986372ff --- /dev/null +++ b/blueprints/reef-keygen/template.toml @@ -0,0 +1,11 @@ +[variables] +PORT = "48765" + +[[config.domains]] +serviceName = "reef-keygen" +port = 48765 +host = "${domain}" +path = "/" + +[config.env] +PORT = "48765" diff --git a/blueprints/rote/template.toml b/blueprints/rote/template.toml index 90dcc8ad7..f780877e3 100644 --- a/blueprints/rote/template.toml +++ b/blueprints/rote/template.toml @@ -5,6 +5,7 @@ postgres_password = "${password:32}" image_tag = "latest" [config] +mounts = [] [[config.domains]] serviceName = "rote-frontend" port = 80 @@ -19,5 +20,3 @@ host = "${backend_domain}" POSTGRES_PASSWORD = "${postgres_password}" IMAGE_TAG = "${image_tag}" VITE_API_BASE = "http://${backend_domain}" - -[[config.mounts]] diff --git a/blueprints/rss-bridge/template.toml b/blueprints/rss-bridge/template.toml index fb06f524a..aee85a41d 100644 --- a/blueprints/rss-bridge/template.toml +++ b/blueprints/rss-bridge/template.toml @@ -2,11 +2,10 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "rss-bridge" port = 80 host = "${main_domain}" [config.env] - -[[config.mounts]] diff --git a/blueprints/rsshub/docker-compose.yml b/blueprints/rsshub/docker-compose.yml index cd36d6c8a..057c32722 100644 --- a/blueprints/rsshub/docker-compose.yml +++ b/blueprints/rsshub/docker-compose.yml @@ -5,7 +5,7 @@ services: # * (consumes more disk space and memory) leave everything unchanged image: diygod/rsshub restart: always - ports: + expose: - 1200 environment: NODE_ENV: production @@ -47,4 +47,4 @@ services: start_period: 5s volumes: - redis-data: \ No newline at end of file + redis-data: diff --git a/blueprints/rustdesk/docker-compose.yml b/blueprints/rustdesk/docker-compose.yml index a29d64d46..684b6c25e 100644 --- a/blueprints/rustdesk/docker-compose.yml +++ b/blueprints/rustdesk/docker-compose.yml @@ -1,3 +1,4 @@ +# dokploy: allow-host-ports — RustDesk relay/rendezvous protocol ports (21115-21119) must be host-published; Traefik cannot route raw TCP/UDP. services: hbbs: image: rustdesk/rustdesk-server:latest diff --git a/blueprints/rustdesk/template.toml b/blueprints/rustdesk/template.toml index 7f44194f2..8083dd364 100644 --- a/blueprints/rustdesk/template.toml +++ b/blueprints/rustdesk/template.toml @@ -3,12 +3,10 @@ server_domain = "${domain}" encryption_key = "${password:32}" [config] - +mounts = [] [config.env] RELAY_HOST = "${server_domain}" RUSTDESK_RELAY_SERVER = "${server_domain}:21117" RUSTDESK_API_SERVER = "http://${server_domain}:21118" RUSTDESK_ID_SERVER = "${server_domain}:21116" ENCRYPTION_KEY = "${encryption_key}" - -[[config.mounts]] \ No newline at end of file diff --git a/blueprints/rustrak-full/docker-compose.yml b/blueprints/rustrak-full/docker-compose.yml index 16f734c7c..fa35356fe 100644 --- a/blueprints/rustrak-full/docker-compose.yml +++ b/blueprints/rustrak-full/docker-compose.yml @@ -12,7 +12,7 @@ services: - DATABASE_URL=postgres://rustrak:${POSTGRES_PASSWORD}@postgres:5432/rustrak - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} - CREATE_SUPERUSER=${CREATE_SUPERUSER} - ports: + expose: - 8080 depends_on: postgres: @@ -23,7 +23,7 @@ services: restart: unless-stopped environment: - RUSTRAK_API_URL=${RUSTRAK_API_URL} - ports: + expose: - 3000 depends_on: - rustrak-server diff --git a/blueprints/rustrak/docker-compose.yml b/blueprints/rustrak/docker-compose.yml index e4c52b0f0..1203e9c12 100644 --- a/blueprints/rustrak/docker-compose.yml +++ b/blueprints/rustrak/docker-compose.yml @@ -13,8 +13,7 @@ services: - CREATE_SUPERUSER=${CREATE_SUPERUSER} volumes: - rustrak_data:/data - ports: + expose: - 8080 - volumes: rustrak_data: diff --git a/blueprints/rutorrent/template.toml b/blueprints/rutorrent/template.toml index 234c807bd..26a6c3a75 100644 --- a/blueprints/rutorrent/template.toml +++ b/blueprints/rutorrent/template.toml @@ -6,6 +6,7 @@ incoming_port = "${INCOMING_PORT:-50000}" dht_port = "${DHT_PORT:-6881}" [config] +mounts = [] # Dokploy will route this domain to the container's internal port 8080 [[config.domains]] serviceName = "rutorrent" @@ -17,18 +18,3 @@ PUID = "${puid}" PGID = "${pgid}" INCOMING_PORT = "${incoming_port}" DHT_PORT = "${dht_port}" - -[[config.mounts]] -type = "volume" -source = "rutorrent-data" -target = "/data" - -[[config.mounts]] -type = "volume" -source = "rutorrent-downloads" -target = "/downloads" - -[[config.mounts]] -type = "volume" -source = "rutorrent-passwd" -target = "/passwd" diff --git a/blueprints/ryot/docker-compose.yml b/blueprints/ryot/docker-compose.yml index 09a727071..44c284599 100644 --- a/blueprints/ryot/docker-compose.yml +++ b/blueprints/ryot/docker-compose.yml @@ -34,4 +34,4 @@ services: restart: unless-stopped volumes: - ryot-postgres-data: \ No newline at end of file + ryot-postgres-data: diff --git a/blueprints/scrutiny/docker-compose.yml b/blueprints/scrutiny/docker-compose.yml index 56b27bd26..9ade2efc3 100644 --- a/blueprints/scrutiny/docker-compose.yml +++ b/blueprints/scrutiny/docker-compose.yml @@ -1,11 +1,12 @@ +version: "3.8" + services: scrutiny: restart: unless-stopped - container_name: scrutiny - image: ghcr.io/analogj/scrutiny:master-omnibus + image: ghcr.io/analogj/scrutiny:v0.8.3-omnibus cap_add: - SYS_RAWIO - ports: + expose: - 8080 # webapp - 8086 # influxDB admin volumes: diff --git a/blueprints/scrutiny/meta.json b/blueprints/scrutiny/meta.json index 515caaa9a..9b862a642 100644 --- a/blueprints/scrutiny/meta.json +++ b/blueprints/scrutiny/meta.json @@ -1,7 +1,7 @@ { "id": "scrutiny", "name": "Scrutiny", - "version": "latest", + "version": "v0.8.3-omnibus", "description": "Hard Drive S.M.A.R.T Monitoring, Historical Trends & Real World Failure Thresholds", "logo": "scrutiny.png", "links": { diff --git a/blueprints/seafile/docker-compose.yml b/blueprints/seafile/docker-compose.yml index 74636b625..d503f50b9 100644 --- a/blueprints/seafile/docker-compose.yml +++ b/blueprints/seafile/docker-compose.yml @@ -20,8 +20,6 @@ services: - MARIADB_AUTO_UPGRADE=1 volumes: - seafile-mysql-db:/var/lib/mysql" - networks: - - seafile-net healthcheck: test: [ @@ -39,8 +37,6 @@ services: memcached: image: memcached:1.6.29 entrypoint: memcached -m 256 - networks: - - seafile-net healthcheck: test: [ @@ -78,18 +74,12 @@ services: condition: service_healthy memcached: condition: service_started - networks: - - seafile-net healthcheck: test: ["CMD", "curl", "-f", "http://127.0.0.1:80/api2/ping"] interval: 20s timeout: 5s retries: 10 -networks: - seafile-net: - name: seafile-net - volumes: seafile-mysql-db: seafile-data: diff --git a/blueprints/seanime/docker-compose.yml b/blueprints/seanime/docker-compose.yml index 9f6992a0a..d68e5f60a 100644 --- a/blueprints/seanime/docker-compose.yml +++ b/blueprints/seanime/docker-compose.yml @@ -23,4 +23,4 @@ services: - ./seanime-config:/home/seanime/.config/Seanime - ./anime:/anime - ./downloads:/downloads - restart: unless-stopped \ No newline at end of file + restart: unless-stopped diff --git a/blueprints/senddock/docker-compose.yml b/blueprints/senddock/docker-compose.yml index c4ff03d0f..6f7bd2a04 100644 --- a/blueprints/senddock/docker-compose.yml +++ b/blueprints/senddock/docker-compose.yml @@ -27,7 +27,7 @@ services: retries: 5 senddock: - image: ghcr.io/arkhe-systems/senddock:0.6.5.1 + image: ghcr.io/arkhe-systems/senddock:latest restart: unless-stopped depends_on: postgres: @@ -40,8 +40,6 @@ services: - 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 diff --git a/blueprints/senddock/meta.json b/blueprints/senddock/meta.json index f7ca8736e..a91cbcd66 100644 --- a/blueprints/senddock/meta.json +++ b/blueprints/senddock/meta.json @@ -1,7 +1,7 @@ { "id": "senddock", "name": "SendDock", - "version": "0.6.5.1", + "version": "0.8.0", "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": { diff --git a/blueprints/senddock/template.toml b/blueprints/senddock/template.toml index 4a8c456e8..3ce8d1343 100644 --- a/blueprints/senddock/template.toml +++ b/blueprints/senddock/template.toml @@ -7,8 +7,7 @@ jwt_secret = "${password:64}" env = [ "POSTGRES_PASSWORD=${postgres_password}", "JWT_SECRET=${jwt_secret}", - "PUBLIC_URL=https://${main_domain}", - "SENDDOCK_LICENSE_KEY=" + "PUBLIC_URL=https://${main_domain}" ] [[config.domains]] diff --git a/blueprints/seq/docker-compose.yml b/blueprints/seq/docker-compose.yml index 2f9aa3def..4ce715229 100644 --- a/blueprints/seq/docker-compose.yml +++ b/blueprints/seq/docker-compose.yml @@ -14,4 +14,4 @@ services: - seq-data:/data volumes: - seq-data: {} \ No newline at end of file + seq-data: {} diff --git a/blueprints/sftpgo/docker-compose.yml b/blueprints/sftpgo/docker-compose.yml index 2cd48773a..429cef8d6 100644 --- a/blueprints/sftpgo/docker-compose.yml +++ b/blueprints/sftpgo/docker-compose.yml @@ -5,10 +5,9 @@ services: volumes: - sftpgo_data:/srv/sftpgo - sftpgo_home:/var/lib/sftpgo - ports: + expose: - "8080" - "2022" - volumes: sftpgo_data: sftpgo_home: diff --git a/blueprints/signoz/docker-compose.yml b/blueprints/signoz/docker-compose.yml index 67ef17710..e73908bc6 100644 --- a/blueprints/signoz/docker-compose.yml +++ b/blueprints/signoz/docker-compose.yml @@ -99,7 +99,7 @@ services: image: signoz/signoz:v0.97.1 command: - --config=/root/config/prometheus.yml - ports: + expose: - "8080" volumes: - ../files/signoz/prometheus.yml:/root/config/prometheus.yml @@ -130,7 +130,7 @@ services: - --config=/etc/otel-collector-config.yaml volumes: - ../files/collector/otel-collector-config.yaml:/etc/otel-collector-config.yaml - ports: + expose: - "4317" # OTLP gRPC receiver - "4318" # OTLP HTTP receiver depends_on: diff --git a/blueprints/silex/docker-compose.yml b/blueprints/silex/docker-compose.yml index bc821dca8..ba14def24 100644 --- a/blueprints/silex/docker-compose.yml +++ b/blueprints/silex/docker-compose.yml @@ -2,7 +2,7 @@ services: silex: image: silexlabs/silex:3.7.0 restart: unless-stopped - ports: + expose: - 6805 environment: - SILEX_URL=${SILEX_URL} diff --git a/blueprints/silverbullet/template.toml b/blueprints/silverbullet/template.toml index 3885edcbb..995a92f89 100644 --- a/blueprints/silverbullet/template.toml +++ b/blueprints/silverbullet/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" sb_user = "admin:${password:16}" [config] +mounts = [] [[config.domains]] serviceName = "silverbullet" port = 3000 @@ -10,7 +11,3 @@ host = "${main_domain}" [config.env] SB_USER = "${sb_user}" - -[[config.mounts]] -name = "silverbullet-space" -mountPath = "/space" diff --git a/blueprints/slash/docker-compose.yml b/blueprints/slash/docker-compose.yml index c3aba95ac..34995246b 100644 --- a/blueprints/slash/docker-compose.yml +++ b/blueprints/slash/docker-compose.yml @@ -32,4 +32,4 @@ services: volumes: slash-app-data: - slash-postgres-data: \ No newline at end of file + slash-postgres-data: diff --git a/blueprints/snapp/docker-compose.yml b/blueprints/snapp/docker-compose.yml index fec99b75f..bc7aaa0cc 100644 --- a/blueprints/snapp/docker-compose.yml +++ b/blueprints/snapp/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: snapp: image: uraniadev/snapp:0.9-rc-020 - ports: + expose: - 3000 environment: - DATABASE_URL=${DATABASE_URL} diff --git a/blueprints/spacedrive/docker-compose.yml b/blueprints/spacedrive/docker-compose.yml index b98d55abf..c12ddad9e 100644 --- a/blueprints/spacedrive/docker-compose.yml +++ b/blueprints/spacedrive/docker-compose.yml @@ -1,7 +1,7 @@ services: server: image: ghcr.io/spacedriveapp/spacedrive/server:latest - ports: + expose: - 8080 environment: - SD_AUTH=${SD_USERNAME}:${SD_PASSWORD} diff --git a/blueprints/spacetimedb/docker-compose.yml b/blueprints/spacetimedb/docker-compose.yml index 05d85869f..2c207b788 100644 --- a/blueprints/spacetimedb/docker-compose.yml +++ b/blueprints/spacetimedb/docker-compose.yml @@ -4,7 +4,7 @@ services: image: clockworklabs/spacetime:v2.6.1 restart: unless-stopped command: start - ports: + expose: - 3000 volumes: - spacetimedb-data:/home/spacetime diff --git a/blueprints/speedtest-tracker/docker-compose.yml b/blueprints/speedtest-tracker/docker-compose.yml index 234eddb4c..12fb68abf 100644 --- a/blueprints/speedtest-tracker/docker-compose.yml +++ b/blueprints/speedtest-tracker/docker-compose.yml @@ -9,8 +9,7 @@ services: DB_CONNECTION: sqlite volumes: - speedtest_config:/config - ports: + expose: - "80" - volumes: speedtest_config: diff --git a/blueprints/spliit/docker-compose.yml b/blueprints/spliit/docker-compose.yml index 69b4acd9d..1d69bcc28 100644 --- a/blueprints/spliit/docker-compose.yml +++ b/blueprints/spliit/docker-compose.yml @@ -25,4 +25,4 @@ services: retries: 5 restart: unless-stopped env_file: - - .env \ No newline at end of file + - .env diff --git a/blueprints/stack-auth/docker-compose.yml b/blueprints/stack-auth/docker-compose.yml index 461b23d39..40f5298f6 100644 --- a/blueprints/stack-auth/docker-compose.yml +++ b/blueprints/stack-auth/docker-compose.yml @@ -18,7 +18,6 @@ services: stack-auth: image: stackauth/server:latest - container_name: stack-auth environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} diff --git a/blueprints/stalwart/docker-compose.yml b/blueprints/stalwart/docker-compose.yml index 67432f183..41cac0d91 100644 --- a/blueprints/stalwart/docker-compose.yml +++ b/blueprints/stalwart/docker-compose.yml @@ -1,7 +1,7 @@ services: stalwart-mail: image: stalwartlabs/stalwart:latest-alpine # for production choose specific version from https://hub.docker.com/r/stalwartlabs/stalwart/tags - ports: + expose: - "443" # HTTPS - "8080" # HTTP API - "25" # SMTP @@ -13,8 +13,10 @@ services: - "110" # POP3 - "995" # POP3S volumes: - - stalwart_data:/opt/stalwart-mail + - stalwart_etc:/etc/stalwart + - stalwart_data:/var/lib/stalwart restart: unless-stopped volumes: - stalwart_data: \ No newline at end of file + stalwart_etc: + stalwart_data: diff --git a/blueprints/statping-ng/docker-compose.yml b/blueprints/statping-ng/docker-compose.yml index a900a6963..e1b63e993 100644 --- a/blueprints/statping-ng/docker-compose.yml +++ b/blueprints/statping-ng/docker-compose.yml @@ -8,5 +8,5 @@ services: - DB_CONN=sqlite volumes: - ../files/statping-ng:/app - ports: + expose: - 8080 diff --git a/blueprints/statping-ng/template.toml b/blueprints/statping-ng/template.toml index fcadac96e..57ff9ec9e 100644 --- a/blueprints/statping-ng/template.toml +++ b/blueprints/statping-ng/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "statping-ng" port = 8080 @@ -10,7 +11,3 @@ host = "${main_domain}" [config.env] TZ = "UTC" DB_CONN = "sqlite" - -[[config.mounts]] -source = "../files/statping-ng" -target = "/app" \ No newline at end of file diff --git a/blueprints/stirling/docker-compose.yml b/blueprints/stirling/docker-compose.yml index 756b07482..6947fd6b5 100644 --- a/blueprints/stirling/docker-compose.yml +++ b/blueprints/stirling/docker-compose.yml @@ -1,7 +1,7 @@ services: stirling-pdf: image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest - ports: + expose: - 8080 volumes: - stirling_pdf_trainingdata:/usr/share/tessdata # Required for extra OCR languages @@ -17,4 +17,4 @@ volumes: stirling_pdf_extraconfigs: stirling_pdf_customfiles: stirling_pdf_logs: - stirling_pdf_pipeline: \ No newline at end of file + stirling_pdf_pipeline: diff --git a/blueprints/storyden/template.toml b/blueprints/storyden/template.toml index 29e2d90d7..31105d7d4 100644 --- a/blueprints/storyden/template.toml +++ b/blueprints/storyden/template.toml @@ -2,6 +2,7 @@ main_domain = "${domain}" [config] +mounts = [] [[config.domains]] serviceName = "storyden" port = 8000 @@ -9,5 +10,3 @@ host = "${main_domain}" [config.env] STORYDEN_FQDN = "http://${main_domain}" - -[[config.mounts]] diff --git a/blueprints/streamflow/docker-compose.yml b/blueprints/streamflow/docker-compose.yml index cbfa8b519..a0a25a8d5 100644 --- a/blueprints/streamflow/docker-compose.yml +++ b/blueprints/streamflow/docker-compose.yml @@ -13,9 +13,8 @@ services: - streamflow-db:/app/db - streamflow-logs:/app/logs - streamflow-uploads:/app/public/uploads - ports: + expose: - 7575 - volumes: streamflow-db: {} streamflow-logs: {} diff --git a/blueprints/streamflow/template.toml b/blueprints/streamflow/template.toml index 3867afaa2..ee4df7854 100644 --- a/blueprints/streamflow/template.toml +++ b/blueprints/streamflow/template.toml @@ -3,6 +3,7 @@ main_domain = "${domain}" session_secret = "${password:64}" [config] +mounts = [] [[config.domains]] serviceName = "streamflow" port = 7575 @@ -11,5 +12,3 @@ host = "${main_domain}" [config.env] SESSION_SECRET = "${session_secret}" TIMEZONE = "Asia/Jakarta" - -[[config.mounts]] diff --git a/blueprints/supabase/docker-compose.yml b/blueprints/supabase/docker-compose.yml index f6fed3b50..02af67da7 100644 --- a/blueprints/supabase/docker-compose.yml +++ b/blueprints/supabase/docker-compose.yml @@ -11,7 +11,7 @@ name: supabase services: studio: - image: supabase/studio:2026.06.03-sha-0bca601 + image: supabase/studio:2026.08.03-sha-022b374 restart: unless-stopped healthcheck: test: @@ -23,9 +23,6 @@ services: interval: 5s retries: 5 start_period: 30s - depends_on: - analytics: - condition: service_healthy environment: HOSTNAME: "0.0.0.0" @@ -35,6 +32,9 @@ services: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + # See: https://supabase.com/docs/guides/self-hosting/remove-superuser-access + POSTGRES_USER_READ_WRITE: postgres + PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY} PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} @@ -49,14 +49,12 @@ services: SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} AUTH_JWT_SECRET: ${JWT_SECRET} + SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-} + SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-} - LOGFLARE_API_KEY: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + # Analytics (Logflare) was removed from the upstream self-hosted stack + ENABLED_FEATURES_LOGS_ALL: "false" - LOGFLARE_URL: http://analytics:4000 - NEXT_PUBLIC_ENABLE_LOGS: "true" - NEXT_ANALYTICS_BACKEND_PROVIDER: postgres SNIPPETS_MANAGEMENT_FOLDER: /app/snippets EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions volumes: @@ -64,7 +62,7 @@ services: - ../files/volumes/functions:/app/edge-functions:Z kong: - image: kong/kong:3.9.1 + image: kong/kong:3.9.3 restart: unless-stopped healthcheck: test: ["CMD", "kong", "health"] @@ -84,8 +82,10 @@ services: environment: KONG_DATABASE: "off" KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml + KONG_ROUTER_FLAVOR: expressions KONG_DNS_ORDER: LAST,A,CNAME KONG_DNS_NOT_FOUND_TTL: 1 + KONG_DNS_VALID_TTL: 5 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 @@ -138,6 +138,7 @@ services: GOTRUE_JWT_EXP: ${JWT_EXPIRY} GOTRUE_JWT_SECRET: ${JWT_SECRET} GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]} + GOTRUE_JWT_ISSUER: ${API_EXTERNAL_URL} GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} @@ -163,13 +164,21 @@ services: depends_on: db: condition: service_healthy + healthcheck: + test: [ "CMD", "postgrest", "--ready" ] + interval: 5s + timeout: 5s + retries: 3 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_ADMIN_SERVER_PORT: 3001 + PGRST_ADMIN_SERVER_HOST: localhost + # Accepts a plain-text symmetric secret, a single JWK, or a JWKS. + PGRST_JWT_SECRET: ${JWT_JWKS:-${JWT_SECRET}} PGRST_DB_USE_LEGACY_GUCS: "false" PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET} PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY} @@ -202,8 +211,11 @@ services: DB_PASSWORD: ${POSTGRES_PASSWORD} DB_NAME: ${POSTGRES_DB} DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' - DB_ENC_KEY: supabaserealtime + DB_ENC_KEY: ${REALTIME_DB_ENC_KEY:-supabaserealtime} + # Legacy symmetric HS256 key API_JWT_SECRET: ${JWT_SECRET} + # JWKS for token verification (EC public + legacy symmetric) + API_JWT_JWKS: ${JWT_JWKS:-{"keys":[]}} SECRET_KEY_BASE: ${SECRET_KEY_BASE} METRICS_JWT_SECRET: ${JWT_SECRET} ERL_AFLAGS: -proto_dist inet_tcp @@ -244,7 +256,10 @@ services: ANON_KEY: ${ANON_KEY} SERVICE_KEY: ${SERVICE_ROLE_KEY} POSTGREST_URL: http://rest:3000 + # Legacy symmetric HS256 key AUTH_JWT_SECRET: ${JWT_SECRET} + # JWKS for token verification (EC public + legacy symmetric) + JWT_JWKS: ${JWT_JWKS:-{"keys":[]}} 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" @@ -292,7 +307,8 @@ services: PG_META_DB_HOST: ${POSTGRES_HOST} PG_META_DB_PORT: ${POSTGRES_PORT} PG_META_DB_NAME: ${POSTGRES_DB} - PG_META_DB_USER: supabase_admin + # See: https://supabase.com/docs/guides/self-hosting/remove-superuser-access + PG_META_DB_USER: postgres PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} CRYPTO_KEY: ${PG_META_CRYPTO_KEY} @@ -306,7 +322,10 @@ services: kong: condition: service_healthy environment: + # Legacy symmetric HS256 key JWT_SECRET: ${JWT_SECRET} + # JWKS for token verification (EC public + legacy symmetric) + SUPABASE_JWKS: ${JWT_JWKS:-{"keys":[]}} SUPABASE_URL: http://kong:8000 SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} @@ -322,43 +341,9 @@ services: "/home/deno/functions/main" ] - analytics: - image: supabase/logflare:1.36.1 - restart: unless-stopped - expose: - - 4000 - # First boot: Logflare seeds _analytics in Postgres; allow extra time before marking unhealthy. - healthcheck: - test: - [ - "CMD-SHELL", - "curl -sSfL -o /dev/null http://localhost:4000/health" - ] - timeout: 10s - interval: 5s - retries: 15 - start_period: 90s - depends_on: - db: - condition: service_healthy - environment: - LOGFLARE_NODE_HOST: 127.0.0.1 - DB_USERNAME: supabase_admin - DB_DATABASE: _supabase - DB_HOSTNAME: ${POSTGRES_HOST} - DB_PORT: ${POSTGRES_PORT} - DB_PASSWORD: ${POSTGRES_PASSWORD} - DB_SCHEMA: _analytics - 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 - POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase - POSTGRES_BACKEND_SCHEMA: _analytics - LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true - db: + # Major upgrade from Postgres 15 requires a dump/restore for existing installs. + # See: https://github.com/orgs/supabase/discussions/46080 image: supabase/postgres:17.6.1.136 restart: unless-stopped volumes: @@ -385,9 +370,6 @@ services: timeout: 5s retries: 15 start_period: 60s - depends_on: - vector: - condition: service_healthy environment: POSTGRES_HOST: /var/run/postgresql PGPORT: ${POSTGRES_PORT} @@ -407,36 +389,6 @@ services: "log_min_messages=fatal" ] - vector: - image: timberio/vector:0.53.0-alpine - restart: unless-stopped - volumes: - - ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z - - ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro,z - healthcheck: - test: - [ - "CMD", - "wget", - "--no-verbose", - "--tries=1", - "--spider", - "http://vector:9001/health" - ] - timeout: 5s - interval: 5s - retries: 5 - start_period: 10s - environment: - LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - command: - [ - "--config", - "/etc/vector/vector.yml" - ] - security_opt: - - "label=disable" - supavisor: image: supabase/supavisor:2.9.5 restart: unless-stopped diff --git a/blueprints/supabase/instructions.md b/blueprints/supabase/instructions.md index af95f2136..bcae4fd35 100644 --- a/blueprints/supabase/instructions.md +++ b/blueprints/supabase/instructions.md @@ -23,6 +23,52 @@ To connect an application (for example with `supabase-js`): - **anon key**: the value of `ANON_KEY` in the Environment tab - **service_role key**: the value of `SERVICE_ROLE_KEY` in the Environment tab (server-side only, never expose it to browsers) +### New API keys (`sb_publishable_…` / `sb_secret_…`) + +Dokploy also generates the newer opaque API keys, so you can use either style: + +- **publishable key**: the value of `SUPABASE_PUBLISHABLE_KEY` (browser-safe, replaces the anon key) +- **secret key**: the value of `SUPABASE_SECRET_KEY` (server-side only, replaces the service_role key) + +Kong exchanges these for the matching JWT before the request reaches Supabase, +so clients never hold a decodable token. Both styles stay valid at the same time +— existing apps on `ANON_KEY` / `SERVICE_ROLE_KEY` keep working. + +## Optional: sign tokens with an ES256 key pair + +Everything is signed with the symmetric `JWT_SECRET` (HS256) by default. Moving +to an asymmetric key pair needs an EC P-256 key, which Dokploy's variable +helpers cannot generate, so `JWT_KEYS` and `JWT_JWKS` ship empty. To switch: + +1. Clone the Supabase repo and go to its `docker/` directory: + + ```bash + git clone --depth 1 https://github.com/supabase/supabase + cd supabase/docker + ``` + +2. Put **this deployment's** `JWT_SECRET` (from the Environment tab) into a local `.env`: + + ```bash + echo "JWT_SECRET=" > .env + ``` + +3. Generate the keys: + + ```bash + sh utils/add-new-auth-keys.sh + ``` + +4. Replace all six values in the Environment tab with the ones it prints, then + redeploy: `SUPABASE_PUBLISHABLE_KEY`, `SUPABASE_SECRET_KEY`, + `ANON_KEY_ASYMMETRIC`, `SERVICE_ROLE_KEY_ASYMMETRIC`, `JWT_KEYS`, `JWT_JWKS`. + +Set them **all together**. `JWT_KEYS` makes Auth sign tokens with ES256, while +`JWT_JWKS` is what PostgREST, Realtime, Storage and Edge Functions use to verify +them — filling in one without the other makes every authenticated request fail. + +See . + ## Recommended configuration Review these variables in the **Environment** tab before using Supabase in production: diff --git a/blueprints/supabase/meta.json b/blueprints/supabase/meta.json index d5d2cdadf..85a41d433 100644 --- a/blueprints/supabase/meta.json +++ b/blueprints/supabase/meta.json @@ -1,7 +1,7 @@ { "id": "supabase", "name": "SupaBase", - "version": "2026.06.03 / dokploy >= 0.22.5", + "version": "2026.08.03-2 / dokploy >= 0.22.5", "description": "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. This require at least version 0.22.5 of dokploy.", "links": { "github": "https://github.com/supabase/supabase", diff --git a/blueprints/supabase/template.toml b/blueprints/supabase/template.toml index 0e76b7dd4..ea4d243fe 100644 --- a/blueprints/supabase/template.toml +++ b/blueprints/supabase/template.toml @@ -2,13 +2,14 @@ main_domain = "${domain}" postgres_password = "${password:32}" dashboard_password = "${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}" +# Realtime requires this to be exactly 16 characters (dokploy's hash helper +# emits one hex character per unit of length). +realtime_db_enc_key = "${hash:16}" jwt_secret = "${password:32}" pooler_tenant_id = "${uuid}" anon_key_payload = """{ @@ -23,6 +24,13 @@ service_role_key_payload = """{ "exp": ${timestamps:2030-01-01T00:00:00Z} } """ +# Defined here (not inline in env) so each JWT is generated once and can be +# reused: Kong swaps the opaque API keys below for these exact tokens. +anon_key = "${jwt:jwt_secret:anon_key_payload}" +service_role_key = "${jwt:jwt_secret:service_role_key_payload}" +# Opaque API keys, shaped like Supabase's: sb__<22 chars>_<8 char suffix>. +publishable_key = "sb_publishable_${hash:22}_${hash:8}" +secret_key = "sb_secret_${hash:22}_${hash:8}" [[config.domains]] serviceName = "kong" @@ -51,16 +59,49 @@ env = [ '', 'SUPABASE_HOST=${main_domain}', 'POSTGRES_PASSWORD=${postgres_password}', +'', +'# Symmetric HS256 key and the legacy API keys derived from it.', 'JWT_SECRET=${jwt_secret}', -'ANON_KEY=${jwt:jwt_secret:anon_key_payload}', -'SERVICE_ROLE_KEY=${jwt:jwt_secret:service_role_key_payload}', +'ANON_KEY=${anon_key}', +'SERVICE_ROLE_KEY=${service_role_key}', +'', +'############', +'# New API keys. These are opaque strings: clients never see a decodable JWT,', +'# Kong swaps them for the *_ASYMMETRIC tokens below before proxying.', +'#', +'# Those tokens are HS256, signed with JWT_SECRET, so they verify against the', +'# same key everything else already uses. Real ES256 keys would need an EC P-256', +'# keypair, which the dokploy variable helpers cannot generate - see JWT_KEYS /', +'# JWT_JWKS below if you want to switch to them.', +'############', +'SUPABASE_PUBLISHABLE_KEY=${publishable_key}', +'SUPABASE_SECRET_KEY=${secret_key}', +'ANON_KEY_ASYMMETRIC=${anon_key}', +'SERVICE_ROLE_KEY_ASYMMETRIC=${service_role_key}', +'', +'############', +'# Optional: move signing to an ES256 key pair. Leave both empty to stay on', +'# HS256 (the default, and what the keys above are signed with).', +'#', +'# To switch, clone https://github.com/supabase/supabase, put this deployment', +'# JWT_SECRET in docker/.env, run `sh utils/add-new-auth-keys.sh`, then replace', +'# all six values above and below with the ones it prints. Set them together:', +'# JWT_KEYS makes Auth sign with ES256, JWT_JWKS is how everything else', +'# verifies those tokens, so filling in one without the other breaks auth.', +'# https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys', +'############', +'# JSON array of signing JWKs (EC private + legacy symmetric), used by Auth.', +'JWT_KEYS=[]', +'# JWKS for token verification (EC public + legacy symmetric), used by', +'# PostgREST, Realtime, Storage and Edge Functions.', +'JWT_JWKS=', +'', 'DASHBOARD_USERNAME=supabase', 'DASHBOARD_PASSWORD=${dashboard_password}', 'SECRET_KEY_BASE=${secret_key_base}', 'VAULT_ENC_KEY=${vault_enc_key}', +'REALTIME_DB_ENC_KEY=${realtime_db_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}', '', '', '############', @@ -111,7 +152,6 @@ env = [ 'JWT_EXPIRY=3600', 'DISABLE_SIGNUP=false', 'API_EXTERNAL_URL=https://${main_domain}', -'JWT_KEYS=[]', '', '## Mailer Config', 'MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"', @@ -164,19 +204,7 @@ env = [ '# Functions - Configuration for Functions', '############', '# NOTE: VERIFY_JWT applies to all functions. Per-function VERIFY_JWT is not supported yet.', -'FUNCTIONS_VERIFY_JWT=false', -'', -'', -'############', -'# Logs - Configuration for Logflare', -'############', -'', -'# Docker socket location - this value will differ depending on your OS', -'DOCKER_SOCKET_LOCATION=/var/run/docker.sock', -'', -'# Google Cloud Project details', -'GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID', -'GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER'] +'FUNCTIONS_VERIFY_JWT=false'] [[config.mounts]] filePath = "/volumes/api/kong-entrypoint.sh" @@ -337,7 +365,34 @@ services: - admin - anon - ## Secure REST routes + ## OpenAPI root - admin only + - name: rest-v1-openapi + _comment: 'PostgREST OpenAPI root: /rest/v1/ -> http://rest:3000/ (admin only). See https://github.com/orgs/supabase/discussions/42949' + url: http://rest:3000/ + routes: + - name: rest-v1-openapi-root + strip_path: true + expression: 'http.path == "/rest/v1/"' + plugins: + - name: cors + - 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 + + ## Secure PostgREST routes - name: rest-v1 _comment: 'PostgREST: /rest/v1/* -> http://rest:3000/*' url: http://rest:3000/ @@ -379,7 +434,7 @@ services: - name: cors - name: key-auth config: - hide_credentials: true + hide_credentials: false - name: request-transformer config: add: @@ -425,6 +480,39 @@ services: allow: - admin - anon + + # Block access to /realtime/v1/api/openapi + - name: realtime-v1-rest-openapi + _comment: 'Realtime: /realtime/v1/api/openapi/* -> http://realtime:4000/api/openapi/* (blocked)' + url: http://realtime:4000/api/openapi + protocol: http + routes: + - name: realtime-v1-rest-openapi + strip_path: true + paths: + - /realtime/v1/api/openapi + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + + # Block access to /realtime/v1/api/tenants + - name: realtime-v1-rest-tenants + _comment: 'Realtime: /realtime/v1/api/tenants/* -> http://realtime:4000/api/tenants/* (blocked)' + url: http://realtime:4000/api/tenants + protocol: http + routes: + - name: realtime-v1-rest-tenants + strip_path: true + paths: + - /realtime/v1/api/tenants + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + - name: realtime-v1-rest _comment: 'Realtime: /realtime/v1/api/* -> http://realtime:4000/api/*' url: http://realtime:4000/api @@ -872,22 +960,25 @@ serve(async () => { [[config.mounts]] filePath = "/volumes/functions/main/index.ts" -content = """import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' +content = """import * as jose from 'jsr:@panva/jose@6' console.log('main function started') const JWT_SECRET = Deno.env.get('JWT_SECRET') -const SUPABASE_URL = Deno.env.get('SUPABASE_URL') +const SUPABASE_JWKS = parseJwks(Deno.env.get('SUPABASE_JWKS')) const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' -let SUPABASE_JWT_KEYS: ReturnType | null = null -if (SUPABASE_URL) { +// Only the bare array parsing is checked here, 'jose' does the key validation. +export function parseJwks(raw: string | undefined): jose.JSONWebKeySet | null { + if (!raw) return null 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) + const parsed = JSON.parse(raw) + if (parsed?.keys && Array.isArray(parsed.keys)) { + return parsed as jose.JSONWebKeySet + } + return null + } catch { + return null } } @@ -920,12 +1011,13 @@ async function isValidLegacyJWT(jwt: string): Promise { } async function isValidJWT(jwt: string): Promise { - if (!SUPABASE_JWT_KEYS) { + if (!SUPABASE_JWKS) { console.error('JWKS not available for ES256/RS256 token verification') return false } try { - await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS) + const localJwks = jose.createLocalJWKSet(SUPABASE_JWKS) + await jose.jwtVerify(jwt, localJwks) } catch (e) { console.error('Asymmetric JWT verification error', e) return false @@ -1007,278 +1099,6 @@ Deno.serve(async (req: Request) => { }) """ -[[config.mounts]] -filePath = "/volumes/logs/vector.yml" -content = """api: - enabled: true - address: 0.0.0.0:9001 - -sources: - docker_host: - type: docker_logs - -transforms: - project_logs: - type: remap - inputs: - - docker_host - source: |- - .project = "default" - .event_message = del(.message) - 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) - del(.stream) - del(.label) - del(.image) - del(.host) - del(.stream) - router: - type: route - inputs: - - project_logs - route: - kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"' - auth: '.appname == "supabase-auth"' - rest: '.appname == "supabase-rest"' - realtime: '.appname == "supabase-realtime"' - storage: '.appname == "supabase-storage"' - functions: '.appname == "supabase-functions"' - db: '.appname == "supabase-db"' - # Ignores non nginx errors since they are related with kong booting up - kong_logs: - type: remap - inputs: - - router.kong - source: |- - req, err = parse_nginx_log(.event_message, "combined") - if err == null { - .timestamp = req.timestamp - .metadata.request.headers.referer = req.referer - .metadata.request.headers.user_agent = req.agent - .metadata.request.headers.cf_connecting_ip = req.client - .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 - } - kong_err: - type: remap - inputs: - - router.kong - source: |- - .metadata.request.method = "GET" - .metadata.response.status_code = 200 - parsed, err = parse_nginx_log(.event_message, "error") - if err == null { - .timestamp = parsed.timestamp - .severity = parsed.severity - .metadata.request.host = parsed.host - .metadata.request.headers.cf_connecting_ip = parsed.client - url, err = split(parsed.request, " ") - if err == null { - .metadata.request.method = url[0] - .metadata.request.path = url[1] - .metadata.request.protocol = url[2] - } - } - if err != null { - abort - } - # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency. - auth_logs: - type: remap - inputs: - - router.auth - source: |- - parsed, err = parse_json(.event_message) - if err == null { - .metadata.timestamp = parsed.time - .metadata = merge!(.metadata, parsed) - } - # PostgREST logs are structured so we separate timestamp from message using regex - rest_logs: - type: remap - inputs: - - router.rest - source: |- - parsed, err = parse_regex(.event_message, r'^(?P