diff --git a/.github/ISSUE_TEMPLATE/01_BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/01_BUG_REPORT.yml
index 42df4785e..f0c77577e 100644
--- a/.github/ISSUE_TEMPLATE/01_BUG_REPORT.yml
+++ b/.github/ISSUE_TEMPLATE/01_BUG_REPORT.yml
@@ -9,9 +9,6 @@ body:
> [!IMPORTANT]
> **Please ensure you are using the latest version of Coolify before submitting an issue, as the bug may have already been fixed in a recent update.** (Of course, if you're experiencing an issue on the latest version that wasn't present in a previous version, please let us know.)
- # 💎 Bounty Program (with [algora.io](https://console.algora.io/org/coollabsio/bounties/new))
- - If you would like to prioritize the issue resolution, consider adding a bounty to this issue through our [Bounty Program](https://console.algora.io/org/coollabsio/bounties/new).
-
- type: textarea
attributes:
label: Error Message and Logs
diff --git a/.github/ISSUE_TEMPLATE/02_ENHANCEMENT_BOUNTY.yml b/.github/ISSUE_TEMPLATE/02_ENHANCEMENT_BOUNTY.yml
deleted file mode 100644
index ef26125e0..000000000
--- a/.github/ISSUE_TEMPLATE/02_ENHANCEMENT_BOUNTY.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: 💎 Enhancement Bounty
-description: "Propose a new feature, service, or improvement with an attached bounty."
-title: "[Enhancement]: "
-labels: ["✨ Enhancement", "🔍 Triage"]
-body:
- - type: markdown
- attributes:
- value: |
- > [!IMPORTANT]
- > **This issue template is exclusively for proposing new features, services, or improvements with an attached bounty.** Enhancements without a bounty can be discussed in the appropriate category of [Github Discussions](https://github.com/coollabsio/coolify/discussions).
-
- # 💎 Add a Bounty (with [algora.io](https://console.algora.io/org/coollabsio/bounties/new))
- - [Click here to add the required bounty](https://console.algora.io/org/coollabsio/bounties/new)
-
- - type: dropdown
- attributes:
- label: Request Type
- description: Select the type of request you are making.
- options:
- - New Feature
- - New Service
- - Improvement
- validations:
- required: true
-
- - type: textarea
- attributes:
- label: Description
- description: Provide a detailed description of the feature, improvement, or service you are proposing.
- validations:
- required: true
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 7fd2c358e..e1286eb22 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -22,7 +22,7 @@ ## Category
## Preview
-
+
## AI Assistance
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9aec08420..85fceb28f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -212,7 +212,7 @@ #### Review Process
- Duplicate or superseded work
- Security or quality concerns
-#### Code Quality, Testing, and Bounty Submissions
+#### Code Quality and Testing
All contributions must adhere to the highest standards of code quality and testing:
- **Testing Required**: Every PR must include steps to test your changes. Untested code will not be reviewed or merged.
@@ -220,15 +220,6 @@ #### Code Quality, Testing, and Bounty Submissions
- **Code Standards**: Follow the existing code style, conventions, and patterns in the codebase.
- **No AI-Generated Code**: Do not submit code generated by AI tools without fully understanding and verifying it. AI-generated submissions that are untested or incorrect will be rejected immediately.
-**For PRs that claim bounties:**
-
-- **Eligibility**: Bounty PRs must strictly follow all guidelines above. Untested, poorly described, or non-compliant PRs will not qualify for bounty rewards.
-- **Original Work**: Bounties are for genuine contributions. Submitting AI-generated or copied code solely for bounty claims will result in disqualification and potential removal from contributing.
-- **Quality Standards**: Bounty submissions are held to even higher standards. Ensure comprehensive testing, clear documentation, and alignment with project goals. When maintainers review the changes, they should work as expected (the things mentioned in the PR description plus what the bounty issuer needs).
-- **Claim Process**: Only successfully merged PRs that pass all reviews (core maintainers + bounty issuer) and meet bounty criteria will be awarded. Follow the issue's bounty guidelines precisely.
-- **Prioritization**: Contributor PRs are prioritized over first-time or new contributors.
-- **Developer Experience**: We highly advise beginners to avoid participating in bug bounties for our codebase. Most of the time, they don't know what they are changing, how it affects other parts of the system, or if their changes are even correct.
-- **Review Comments**: When maintainers ask questions, you should be able to respond properly without generic or AI-generated fluff.
## Development Notes
diff --git a/README.md b/README.md
index a5aa69343..9a5feff4e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ # Coolify
An open-source & self-hostable Heroku / Netlify / Vercel alternative.
 [](https://console.algora.io/org/coollabsio/bounties/new)
+)
## About the Project
@@ -65,7 +65,6 @@ ### Huge Sponsors
### Big Sponsors
* [23M](https://23m.com?ref=coolify.io) - Your experts for high-availability hosting solutions!
-* [Algora](https://algora.io?ref=coolify.io) - Open source contribution platform
* [American Cloud](https://americancloud.com?ref=coolify.io) - US-based cloud infrastructure services
* [Arcjet](https://arcjet.com?ref=coolify.io) - Advanced web security and performance solutions
* [BC Direct](https://bc.direct?ref=coolify.io) - Your trusted technology consulting partner
diff --git a/config/constants.php b/config/constants.php
index 828493208..9ce471560 100644
--- a/config/constants.php
+++ b/config/constants.php
@@ -3,8 +3,8 @@
return [
'coolify' => [
'version' => '4.0.0-beta.471',
- 'helper_version' => '1.0.12',
- 'realtime_version' => '1.0.11',
+ 'helper_version' => '1.0.13',
+ 'realtime_version' => '1.0.12',
'self_hosted' => env('SELF_HOSTED', true),
'autoupdate' => env('AUTOUPDATE'),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index 0bd4ae2dd..e6d2bce54 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -60,7 +60,7 @@ services:
retries: 10
timeout: 2s
soketi:
- image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.11'
+ image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.12'
ports:
- "${SOKETI_PORT:-6001}:6001"
- "6002:6002"
diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml
index ca233356a..00734fb0e 100644
--- a/docker-compose.windows.yml
+++ b/docker-compose.windows.yml
@@ -96,7 +96,7 @@ services:
retries: 10
timeout: 2s
soketi:
- image: 'ghcr.io/coollabsio/coolify-realtime:1.0.10'
+ image: 'ghcr.io/coollabsio/coolify-realtime:1.0.12'
pull_policy: always
container_name: coolify-realtime
restart: always
diff --git a/docker/coolify-helper/Dockerfile b/docker/coolify-helper/Dockerfile
index 14879eb96..9c984a5ee 100644
--- a/docker/coolify-helper/Dockerfile
+++ b/docker/coolify-helper/Dockerfile
@@ -28,7 +28,8 @@ ARG NIXPACKS_VERSION
USER root
WORKDIR /artifacts
-RUN apk add --no-cache bash curl git git-lfs openssh-client tar tini
+RUN apk upgrade --no-cache && \
+ apk add --no-cache bash curl git git-lfs openssh-client tar tini
RUN mkdir -p ~/.docker/cli-plugins
RUN if [[ ${TARGETPLATFORM} == 'linux/amd64' ]]; then \
curl -sSL https://github.com/docker/buildx/releases/download/v${DOCKER_BUILDX_VERSION}/buildx-v${DOCKER_BUILDX_VERSION}.linux-amd64 -o ~/.docker/cli-plugins/docker-buildx && \
diff --git a/docker/coolify-realtime/Dockerfile b/docker/coolify-realtime/Dockerfile
index 99157268b..325a30dcc 100644
--- a/docker/coolify-realtime/Dockerfile
+++ b/docker/coolify-realtime/Dockerfile
@@ -10,7 +10,8 @@ ARG TARGETPLATFORM
ARG CLOUDFLARED_VERSION
WORKDIR /terminal
-RUN apk add --no-cache openssh-client make g++ python3 curl
+RUN apk upgrade --no-cache && \
+ apk add --no-cache openssh-client make g++ python3 curl
COPY docker/coolify-realtime/package.json ./
RUN npm i
RUN npm rebuild node-pty --update-binary
diff --git a/docker/development/Dockerfile b/docker/development/Dockerfile
index 98b4d2006..77013e1b9 100644
--- a/docker/development/Dockerfile
+++ b/docker/development/Dockerfile
@@ -33,7 +33,8 @@ RUN docker-php-serversideup-set-id www-data $USER_ID:$GROUP_ID && \
docker-php-serversideup-set-file-permissions --owner $USER_ID:$GROUP_ID --service nginx
# Install PostgreSQL repository and keys
-RUN apk add --no-cache gnupg && \
+RUN apk upgrade --no-cache && \
+ apk add --no-cache gnupg && \
mkdir -p /usr/share/keyrings && \
curl -fSsL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /usr/share/keyrings/postgresql.gpg
diff --git a/other/nightly/docker-compose.prod.yml b/other/nightly/docker-compose.prod.yml
index 0bd4ae2dd..e6d2bce54 100644
--- a/other/nightly/docker-compose.prod.yml
+++ b/other/nightly/docker-compose.prod.yml
@@ -60,7 +60,7 @@ services:
retries: 10
timeout: 2s
soketi:
- image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.11'
+ image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.12'
ports:
- "${SOKETI_PORT:-6001}:6001"
- "6002:6002"
diff --git a/other/nightly/docker-compose.windows.yml b/other/nightly/docker-compose.windows.yml
index ca233356a..00734fb0e 100644
--- a/other/nightly/docker-compose.windows.yml
+++ b/other/nightly/docker-compose.windows.yml
@@ -96,7 +96,7 @@ services:
retries: 10
timeout: 2s
soketi:
- image: 'ghcr.io/coollabsio/coolify-realtime:1.0.10'
+ image: 'ghcr.io/coollabsio/coolify-realtime:1.0.12'
pull_policy: always
container_name: coolify-realtime
restart: always
diff --git a/other/nightly/versions.json b/other/nightly/versions.json
index af11ef4d3..a6b1936e6 100644
--- a/other/nightly/versions.json
+++ b/other/nightly/versions.json
@@ -7,10 +7,10 @@
"version": "4.0.0"
},
"helper": {
- "version": "1.0.12"
+ "version": "1.0.13"
},
"realtime": {
- "version": "1.0.11"
+ "version": "1.0.12"
},
"sentinel": {
"version": "0.0.21"
diff --git a/public/svgs/grimmory.svg b/public/svgs/grimmory.svg
new file mode 100644
index 000000000..cd8230fa2
--- /dev/null
+++ b/public/svgs/grimmory.svg
@@ -0,0 +1,4 @@
+
diff --git a/templates/compose/alexandrie.yaml b/templates/compose/alexandrie.yaml
index 9d7d59227..32bb98b2f 100644
--- a/templates/compose/alexandrie.yaml
+++ b/templates/compose/alexandrie.yaml
@@ -7,7 +7,7 @@
services:
frontend:
- image: ghcr.io/smaug6739/alexandrie-frontend:v8.4.1
+ image: ghcr.io/smaug6739/alexandrie-frontend:v8.7.2
environment:
- SERVICE_URL_FRONTEND_8200
- PORT=8200
@@ -21,7 +21,7 @@ services:
- backend
backend:
- image: ghcr.io/smaug6739/alexandrie-backend:v8.4.1
+ image: ghcr.io/smaug6739/alexandrie-backend:v8.7.2
environment:
- SERVICE_URL_BACKEND_8201
- BACKEND_PORT=8201
@@ -74,7 +74,7 @@ services:
retries: 5
rustfs:
- image: rustfs/rustfs:1.0.0-alpha.81
+ image: rustfs/rustfs:1.0.0-alpha.90
environment:
- SERVICE_URL_RUSTFS_9000
- RUSTFS_ACCESS_KEY=${SERVICE_USER_RUSTFS}
diff --git a/templates/compose/convex.yaml b/templates/compose/convex.yaml
index 49f2449df..e80cc4254 100644
--- a/templates/compose/convex.yaml
+++ b/templates/compose/convex.yaml
@@ -7,7 +7,7 @@
services:
backend:
- image: ghcr.io/get-convex/convex-backend:00bd92723422f3bff968230c94ccdeb8c1719832
+ image: ghcr.io/get-convex/convex-backend:a9a760ca10399ed42e1b4bb87c78539a235488c7
volumes:
- data:/convex/data
environment:
@@ -47,7 +47,7 @@ services:
start_period: 10s
dashboard:
- image: ghcr.io/get-convex/convex-dashboard:33cef775a8a6228cbacee4a09ac2c4073d62ed13
+ image: ghcr.io/get-convex/convex-dashboard:a9a760ca10399ed42e1b4bb87c78539a235488c7
environment:
- SERVICE_URL_DASHBOARD_6791
# URL of the Convex API as accessed by the dashboard (browser).
@@ -56,6 +56,6 @@ services:
backend:
condition: service_healthy
healthcheck:
- test: wget -qO- http://127.0.0.1:6791/
+ test: curl -f http://127.0.0.1:6791/
interval: 5s
start_period: 5s
diff --git a/templates/compose/directus-with-postgresql.yaml b/templates/compose/directus-with-postgresql.yaml
index c35e411fd..763944456 100644
--- a/templates/compose/directus-with-postgresql.yaml
+++ b/templates/compose/directus-with-postgresql.yaml
@@ -27,6 +27,11 @@ services:
- REDIS_HOST=redis
- REDIS_PORT=6379
- WEBSOCKETS_ENABLED=true
+ - CORS_ENABLED=${CORS_ENABLED:-true}
+ - CORS_ORIGIN=${CORS_ORIGIN}
+ - CORS_METHODS=${CORS_METHODS:-GET,POST,PATCH,DELETE,OPTIONS}
+ - CORS_ALLOWED_HEADERS=${CORS_ALLOWED_HEADERS:-Content-Type,Authorization}
+ - CORS_CREDENTIALS=${CORS_CREDENTIALS:-true}
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8055/admin/login"]
interval: 5s
diff --git a/templates/compose/directus.yaml b/templates/compose/directus.yaml
index 36589c72a..1648f7e3d 100644
--- a/templates/compose/directus.yaml
+++ b/templates/compose/directus.yaml
@@ -22,6 +22,11 @@ services:
- DB_CLIENT=sqlite3
- DB_FILENAME=/directus/database/data.db
- WEBSOCKETS_ENABLED=true
+ - CORS_ENABLED=${CORS_ENABLED:-true}
+ - CORS_ORIGIN=${CORS_ORIGIN}
+ - CORS_METHODS=${CORS_METHODS:-GET,POST,PATCH,DELETE,OPTIONS}
+ - CORS_ALLOWED_HEADERS=${CORS_ALLOWED_HEADERS:-Content-Type,Authorization}
+ - CORS_CREDENTIALS=${CORS_CREDENTIALS:-true}
healthcheck:
test:
["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8055/admin/login"]
diff --git a/templates/compose/grimmory.yaml b/templates/compose/grimmory.yaml
new file mode 100644
index 000000000..c04d1086d
--- /dev/null
+++ b/templates/compose/grimmory.yaml
@@ -0,0 +1,49 @@
+# documentation: https://github.com/grimmory-tools/grimmory
+# slogan: Grimmory is a self-hosted application for managing your entire book collection in one place. Organize, read, annotate, sync across devices, and share without relying on third-party services.
+# tags: books,ebooks,library,reader
+# logo: svgs/grimmory.svg
+# port: 80
+
+services:
+ grimmory:
+ image: 'grimmory/grimmory:nightly-20260403-3a371f7' # Released on April 3 2026
+ environment:
+ - SERVICE_URL_GRIMMORY_80
+ - 'USER_ID=${GRIMMORY_USER_ID:-0}'
+ - 'GROUP_ID=${GRIMMORY_GROUP_ID:-0}'
+ - 'TZ=${TZ:-UTC}'
+ - 'DATABASE_URL=jdbc:mariadb://mariadb:3306/${MARIADB_DATABASE:-grimmory-db}'
+ - 'DATABASE_USERNAME=${SERVICE_USER_MARIADB}'
+ - 'DATABASE_PASSWORD=${SERVICE_PASSWORD_MARIADB}'
+ - BOOKLORE_PORT=80
+ volumes:
+ - 'grimmory-data:/app/data'
+ - 'grimmory-books:/books'
+ - 'grimmory-bookdrop:/bookdrop'
+ healthcheck:
+ test: 'wget --no-verbose --tries=1 --spider http://127.0.0.1/health || exit 1'
+ interval: 10s
+ timeout: 5s
+ retries: 10
+ depends_on:
+ mariadb:
+ condition: service_healthy
+
+ mariadb:
+ image: 'mariadb:12'
+ environment:
+ - 'MARIADB_USER=${SERVICE_USER_MARIADB}'
+ - 'MARIADB_PASSWORD=${SERVICE_PASSWORD_MARIADB}'
+ - 'MARIADB_ROOT_PASSWORD=${SERVICE_PASSWORD_MARIADBROOT}'
+ - 'MARIADB_DATABASE=${MARIADB_DATABASE:-grimmory-db}'
+ volumes:
+ - 'mariadb-data:/var/lib/mysql'
+ healthcheck:
+ test:
+ - CMD
+ - healthcheck.sh
+ - '--connect'
+ - '--innodb_initialized'
+ interval: 10s
+ timeout: 5s
+ retries: 10
\ No newline at end of file
diff --git a/templates/compose/librechat.yaml b/templates/compose/librechat.yaml
index 575411443..66ac35aca 100644
--- a/templates/compose/librechat.yaml
+++ b/templates/compose/librechat.yaml
@@ -7,7 +7,7 @@
services:
librechat:
- image: ghcr.io/danny-avila/librechat-dev-api:latest
+ image: ghcr.io/danny-avila/librechat-dev-api:6ecd1b510faaa593ad954fb6276c18e5f12a8e53 # Released on April 2
environment:
- SERVICE_URL_LIBRECHAT_3080
- DOMAIN_CLIENT=${SERVICE_URL_LIBRECHAT}
@@ -64,7 +64,7 @@ services:
"--no-verbose",
"--tries=1",
"--spider",
- "http://127.0.0.1:3080/api/health",
+ "http://127.0.0.1:3080/health",
]
interval: 5s
timeout: 10s
@@ -92,7 +92,7 @@ services:
retries: 3
meilisearch:
- image: getmeili/meilisearch:v1.12.3
+ image: getmeili/meilisearch:v1.35.1
environment:
- MEILI_MASTER_KEY=${SERVICE_PASSWORD_MEILI}
- MEILI_NO_ANALYTICS=${MEILI_NO_ANALYTICS:-false}
@@ -107,7 +107,7 @@ services:
retries: 15
vectordb:
- image: ankane/pgvector:latest
+ image: ankane/pgvector:v0.5.1 # pgvector by ankane is archived and not maintained, in future we have to swap this image to something else that is well maintained
environment:
- POSTGRES_DB=rag
- POSTGRES_USER=${SERVICE_USER_POSTGRES}
@@ -129,7 +129,7 @@ services:
start_period: 10s
rag-api:
- image: ghcr.io/danny-avila/librechat-rag-api-dev-lite:latest
+ image: ghcr.io/danny-avila/librechat-rag-api-dev-lite:v0.7.3
environment:
- POSTGRES_DB=rag
- POSTGRES_USER=${SERVICE_USER_POSTGRES}
diff --git a/templates/compose/minecraft.yaml b/templates/compose/minecraft.yaml
index 507bbb8bb..46ae437bb 100644
--- a/templates/compose/minecraft.yaml
+++ b/templates/compose/minecraft.yaml
@@ -1,6 +1,6 @@
# documentation: https://github.com/itzg/docker-minecraft-server
# slogan: Minecraft Server that will automatically download selected version at startup.
-# category: media
+# category: games
# tags: minecraft
# logo: svgs/minecraft.svg
# port: 25565
diff --git a/templates/compose/n8n-with-postgres-and-worker.yaml b/templates/compose/n8n-with-postgres-and-worker.yaml
index b7d381399..286038551 100644
--- a/templates/compose/n8n-with-postgres-and-worker.yaml
+++ b/templates/compose/n8n-with-postgres-and-worker.yaml
@@ -48,7 +48,7 @@ services:
redis:
condition: service_healthy
healthcheck:
- test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/"]
+ test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/healthz"]
interval: 5s
timeout: 20s
retries: 10
@@ -133,7 +133,7 @@ services:
healthcheck:
test:
- CMD-SHELL
- - 'wget -qO- http://127.0.0.1:5680/'
+ - 'wget -qO- http://127.0.0.1:5680/healthz'
interval: 5s
timeout: 20s
retries: 10
diff --git a/templates/compose/n8n-with-postgresql.yaml b/templates/compose/n8n-with-postgresql.yaml
index d7096add2..4d1f9e970 100644
--- a/templates/compose/n8n-with-postgresql.yaml
+++ b/templates/compose/n8n-with-postgresql.yaml
@@ -41,7 +41,7 @@ services:
postgresql:
condition: service_healthy
healthcheck:
- test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/"]
+ test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/healthz"]
interval: 5s
timeout: 20s
retries: 10
@@ -58,7 +58,7 @@ services:
healthcheck:
test:
- CMD-SHELL
- - 'wget -qO- http://127.0.0.1:5680/'
+ - 'wget -qO- http://127.0.0.1:5680/healthz'
interval: 5s
timeout: 20s
retries: 10
diff --git a/templates/compose/n8n.yaml b/templates/compose/n8n.yaml
index ff5ee90b2..46a1a9fc5 100644
--- a/templates/compose/n8n.yaml
+++ b/templates/compose/n8n.yaml
@@ -32,7 +32,7 @@ services:
volumes:
- n8n-data:/home/node/.n8n
healthcheck:
- test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/"]
+ test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:5678/healthz"]
interval: 5s
timeout: 20s
retries: 10
@@ -49,7 +49,7 @@ services:
healthcheck:
test:
- CMD-SHELL
- - 'wget -qO- http://127.0.0.1:5680/'
+ - 'wget -qO- http://127.0.0.1:5680/healthz'
interval: 5s
timeout: 20s
retries: 10
diff --git a/templates/compose/nextcloud-with-mariadb.yaml b/templates/compose/nextcloud-with-mariadb.yaml
index 42ee52274..60703f862 100644
--- a/templates/compose/nextcloud-with-mariadb.yaml
+++ b/templates/compose/nextcloud-with-mariadb.yaml
@@ -29,7 +29,7 @@ services:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
- interval: 2s
+ interval: 30s
timeout: 10s
retries: 15
diff --git a/templates/compose/nextcloud-with-mysql.yaml b/templates/compose/nextcloud-with-mysql.yaml
index e6512deed..7d3027b9d 100644
--- a/templates/compose/nextcloud-with-mysql.yaml
+++ b/templates/compose/nextcloud-with-mysql.yaml
@@ -29,7 +29,7 @@ services:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
- interval: 2s
+ interval: 30s
timeout: 10s
retries: 15
diff --git a/templates/compose/nextcloud-with-postgres.yaml b/templates/compose/nextcloud-with-postgres.yaml
index b3eaaa853..b720704e2 100644
--- a/templates/compose/nextcloud-with-postgres.yaml
+++ b/templates/compose/nextcloud-with-postgres.yaml
@@ -29,7 +29,7 @@ services:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
- interval: 2s
+ interval: 30s
timeout: 10s
retries: 15
diff --git a/templates/compose/nextcloud.yaml b/templates/compose/nextcloud.yaml
index dfdf5dba3..84c0cf5c2 100644
--- a/templates/compose/nextcloud.yaml
+++ b/templates/compose/nextcloud.yaml
@@ -18,6 +18,6 @@ services:
- nextcloud-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
- interval: 2s
+ interval: 30s
timeout: 10s
retries: 15
diff --git a/templates/compose/rivet-engine.yaml b/templates/compose/rivet-engine.yaml
index 608cdaac3..e77561cca 100644
--- a/templates/compose/rivet-engine.yaml
+++ b/templates/compose/rivet-engine.yaml
@@ -7,14 +7,13 @@
services:
rivet-engine:
- image: rivetkit/engine:25.8.0
+ image: rivetdev/engine:2.2.0
environment:
- SERVICE_URL_RIVET_6420
- - 'RIVET__AUTH__ADMIN_TOKEN=${SERVICE_PASSWORD_RIVET}'
- - RIVET__POSTGRES__URL=postgresql://$SERVICE_USER_POSTGRESQL:$SERVICE_PASSWORD_POSTGRESQL@postgresql:5432/${POSTGRESQL_DATABASE-rivet}
- depends_on:
- postgresql:
- condition: service_healthy
+ - RIVET__FILE_SYSTEM__PATH=/data
+ - 'RIVET__AUTH__ADMIN_TOKEN=${SERVICE_BASE64_TOKEN}'
+ volumes:
+ - 'rivet-data:/data'
healthcheck:
test:
- CMD
@@ -24,19 +23,4 @@ services:
interval: 2s
timeout: 10s
retries: 10
- start_period: 30s
-
- postgresql:
- image: postgres:17-alpine
- volumes:
- - rivet-postgresql-data:/var/lib/postgresql/data
- environment:
- - POSTGRES_USER=${SERVICE_USER_POSTGRESQL}
- - POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRESQL}
- - POSTGRES_DB=${POSTGRESQL_DATABASE-rivet}
- healthcheck:
- test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
- interval: 5s
- timeout: 20s
- retries: 10
-
+ start_period: 30s
\ No newline at end of file
diff --git a/templates/compose/supabase.yaml b/templates/compose/supabase.yaml
index fad059a08..b79eec604 100644
--- a/templates/compose/supabase.yaml
+++ b/templates/compose/supabase.yaml
@@ -8,33 +8,77 @@
services:
supabase-kong:
- image: kong:2.8.1
- # https://unix.stackexchange.com/a/294837
- entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
+ image: kong/kong:3.9.1
+ entrypoint: /home/kong/kong-entrypoint.sh
depends_on:
supabase-analytics:
condition: service_healthy
+ healthcheck:
+ test: ["CMD", "kong", "health"]
+ interval: 5s
+ timeout: 5s
+ retries: 5
environment:
- SERVICE_URL_SUPABASEKONG_8000
- KONG_PORT_MAPS=443:8000
- JWT_SECRET=${SERVICE_PASSWORD_JWT}
- KONG_DATABASE=off
- - KONG_DECLARATIVE_CONFIG=/home/kong/kong.yml
+ - KONG_DECLARATIVE_CONFIG=/usr/local/kong/kong.yml
# https://github.com/supabase/cli/issues/14
- KONG_DNS_ORDER=LAST,A,CNAME
- - KONG_PLUGINS=request-transformer,cors,key-auth,acl,basic-auth
+ - KONG_DNS_NOT_FOUND_TTL=1
+ - KONG_PLUGINS=request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function
- KONG_NGINX_PROXY_PROXY_BUFFER_SIZE=160k
- KONG_NGINX_PROXY_PROXY_BUFFERS=64 160k
+ - 'KONG_PROXY_ACCESS_LOG=/dev/stdout combined'
- SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}
- SUPABASE_SERVICE_KEY=${SERVICE_SUPABASESERVICE_KEY}
+ - SUPABASE_PUBLISHABLE_KEY=${SUPABASE_PUBLISHABLE_KEY:-}
+ - SUPABASE_SECRET_KEY=${SUPABASE_SECRET_KEY:-}
+ - ANON_KEY_ASYMMETRIC=${ANON_KEY_ASYMMETRIC:-}
+ - SERVICE_ROLE_KEY_ASYMMETRIC=${SERVICE_ROLE_KEY_ASYMMETRIC:-}
- DASHBOARD_USERNAME=${SERVICE_USER_ADMIN}
- DASHBOARD_PASSWORD=${SERVICE_PASSWORD_ADMIN}
- 'KONG_STORAGE_CONNECT_TIMEOUT=${KONG_STORAGE_CONNECT_TIMEOUT:-60}'
- 'KONG_STORAGE_WRITE_TIMEOUT=${KONG_STORAGE_WRITE_TIMEOUT:-3600}'
- 'KONG_STORAGE_READ_TIMEOUT=${KONG_STORAGE_READ_TIMEOUT:-3600}'
- 'KONG_STORAGE_REQUEST_BUFFERING=${KONG_STORAGE_REQUEST_BUFFERING:-false}'
- - 'KONG_STORAGE_RESPONSE_BUFFERING=${KONG_STORAGE_RESPONSE_BUFFERING:-false}'
+ - 'KONG_STORAGE_RESPONSE_BUFFERING=${KONG_STORAGE_RESPONSE_BUFFERING:-false}'
volumes:
+ - type: bind
+ source: ./volumes/api/kong-entrypoint.sh
+ target: /home/kong/kong-entrypoint.sh
+ content: |
+ #!/bin/bash
+ # Custom entrypoint for Kong that builds Lua expressions for request-transformer
+ # and performs environment variable substitution in the declarative config.
+
+ if [ -n "$SUPABASE_SECRET_KEY" ] && [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then
+ export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)"
+ export LUA_RT_WS_EXPR="\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)"
+ else
+ export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)"
+ export LUA_RT_WS_EXPR="\$(query_params.apikey)"
+ fi
+
+ awk '{
+ result = ""
+ rest = $0
+ while (match(rest, /\$[A-Za-z_][A-Za-z_0-9]*/)) {
+ varname = substr(rest, RSTART + 1, RLENGTH - 1)
+ if (varname in ENVIRON) {
+ result = result substr(rest, 1, RSTART - 1) ENVIRON[varname]
+ } else {
+ result = result substr(rest, 1, RSTART + RLENGTH - 1)
+ }
+ rest = substr(rest, RSTART + RLENGTH)
+ }
+ print result rest
+ }' /home/kong/temp.yml > "$KONG_DECLARATIVE_CONFIG"
+
+ sed -i '/^[[:space:]]*- key:[[:space:]]*$/d' "$KONG_DECLARATIVE_CONFIG"
+
+ exec /entrypoint.sh kong docker-start
# https://github.com/supabase/supabase/issues/12661
- type: bind
source: ./volumes/api/kong.yml
@@ -51,9 +95,11 @@ services:
- username: anon
keyauth_credentials:
- key: $SUPABASE_ANON_KEY
+ - key: $SUPABASE_PUBLISHABLE_KEY
- username: service_role
keyauth_credentials:
- key: $SUPABASE_SERVICE_KEY
+ - key: $SUPABASE_SECRET_KEY
###
### Access Control List
@@ -69,8 +115,8 @@ services:
###
basicauth_credentials:
- consumer: DASHBOARD
- username: $DASHBOARD_USERNAME
- password: $DASHBOARD_PASSWORD
+ username: '$DASHBOARD_USERNAME'
+ password: '$DASHBOARD_PASSWORD'
###
@@ -106,6 +152,36 @@ services:
- /auth/v1/authorize
plugins:
- name: cors
+ - name: auth-v1-open-jwks
+ _comment: 'Auth: /auth/v1/.well-known/jwks.json -> http://supabase-auth:9999/.well-known/jwks.json'
+ url: http://supabase-auth:9999/.well-known/jwks.json
+ routes:
+ - name: auth-v1-open-jwks
+ strip_path: true
+ paths:
+ - /auth/v1/.well-known/jwks.json
+ plugins:
+ - name: cors
+
+ - name: auth-v1-open-sso-acs
+ url: "http://supabase-auth:9999/sso/saml/acs"
+ routes:
+ - name: auth-v1-open-sso-acs
+ strip_path: true
+ paths:
+ - /sso/saml/acs
+ plugins:
+ - name: cors
+
+ - name: auth-v1-open-sso-metadata
+ url: "http://supabase-auth:9999/sso/saml/metadata"
+ routes:
+ - name: auth-v1-open-sso-metadata
+ strip_path: true
+ paths:
+ - /sso/saml/metadata
+ plugins:
+ - name: cors
## Secure Auth routes
- name: auth-v1
@@ -121,6 +197,14 @@ services:
- name: key-auth
config:
hide_credentials: false
+ - name: request-transformer
+ config:
+ add:
+ headers:
+ - "Authorization: $LUA_AUTH_EXPR"
+ replace:
+ headers:
+ - "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -141,7 +225,15 @@ services:
- name: cors
- name: key-auth
config:
- hide_credentials: true
+ hide_credentials: false
+ - name: request-transformer
+ config:
+ add:
+ headers:
+ - "Authorization: $LUA_AUTH_EXPR"
+ replace:
+ headers:
+ - "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -162,12 +254,17 @@ services:
- name: cors
- name: key-auth
config:
- hide_credentials: true
+ hide_credentials: false
- name: request-transformer
config:
add:
headers:
- - Content-Profile:graphql_public
+ - "Content-Profile: graphql_public"
+ - "Authorization: $LUA_AUTH_EXPR"
+ replace:
+ headers:
+ - "Content-Profile: graphql_public"
+ - "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -190,6 +287,14 @@ services:
- name: key-auth
config:
hide_credentials: false
+ - name: request-transformer
+ config:
+ add:
+ querystring:
+ - "apikey: $LUA_RT_WS_EXPR"
+ replace:
+ querystring:
+ - "apikey: $LUA_RT_WS_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -197,7 +302,7 @@ services:
- admin
- anon
- name: realtime-v1-rest
- _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
+ _comment: 'Realtime: /realtime/v1/api/* -> http://realtime:4000/api/*'
url: http://realtime-dev:4000/api
protocol: http
routes:
@@ -210,6 +315,14 @@ services:
- name: key-auth
config:
hide_credentials: false
+ - name: request-transformer
+ config:
+ add:
+ headers:
+ - "Authorization: $LUA_AUTH_EXPR"
+ replace:
+ headers:
+ - "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -217,7 +330,8 @@ services:
- admin
- anon
- ## Storage routes: the storage server manages its own auth
+ ## Storage API endpoint
+ ## No key-auth - S3 protocol requests don't carry an apikey header.
- name: storage-v1
_comment: 'Storage: /storage/v1/* -> http://supabase-storage:5000/*'
connect_timeout: $KONG_STORAGE_CONNECT_TIMEOUT
@@ -233,11 +347,20 @@ services:
response_buffering: $KONG_STORAGE_RESPONSE_BUFFERING
plugins:
- name: cors
+ - name: post-function
+ config:
+ access:
+ - |
+ local auth = kong.request.get_header("authorization")
+ if auth == nil or auth == "" or auth:find("^%s*$") then
+ kong.service.request.clear_header("authorization")
+ end
## Edge Functions routes
- name: functions-v1
_comment: 'Edge Functions: /functions/v1/* -> http://supabase-edge-functions:9000/*'
url: http://supabase-edge-functions:9000/
+ read_timeout: 150000
routes:
- name: functions-v1-all
strip_path: true
@@ -246,15 +369,28 @@ services:
plugins:
- name: cors
- ## Analytics routes
- - name: analytics-v1
- _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*'
- url: http://supabase-analytics:4000/
+ ## OAuth 2.0 Authorization Server Metadata (RFC 8414)
+ - name: well-known-oauth
+ _comment: 'Auth: /.well-known/oauth-authorization-server -> http://supabase-auth:9999/.well-known/oauth-authorization-server'
+ url: http://supabase-auth:9999/.well-known/oauth-authorization-server
routes:
- - name: analytics-v1-all
+ - name: well-known-oauth
strip_path: true
paths:
- - /analytics/v1/
+ - /.well-known/oauth-authorization-server
+ plugins:
+ - name: cors
+
+ ## Analytics routes
+ ## Not used - Studio and Vector talk directly to analytics via Docker networking.
+ # - name: analytics-v1
+ # _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*'
+ # url: http://supabase-analytics:4000/
+ # routes:
+ # - name: analytics-v1-all
+ # strip_path: true
+ # paths:
+ # - /analytics/v1/
## Secure Database routes
- name: meta
@@ -275,6 +411,48 @@ services:
allow:
- admin
+ ## Block access to /api/mcp
+ - name: mcp-blocker
+ _comment: 'Block direct access to /api/mcp'
+ url: http://supabase-studio:3000/api/mcp
+ routes:
+ - name: mcp-blocker-route
+ strip_path: true
+ paths:
+ - /api/mcp
+ plugins:
+ - name: request-termination
+ config:
+ status_code: 403
+ message: "Access is forbidden."
+
+ ## MCP endpoint - local access
+ - name: mcp
+ _comment: 'MCP: /mcp -> http://supabase-studio:3000/api/mcp (local access)'
+ url: http://supabase-studio:3000/api/mcp
+ routes:
+ - name: mcp
+ strip_path: true
+ paths:
+ - /mcp
+ plugins:
+ # Block access to /mcp by default
+ - name: request-termination
+ config:
+ status_code: 403
+ message: "Access is forbidden."
+ # Enable local access (danger zone!)
+ # 1. Comment out the 'request-termination' section above
+ # 2. Uncomment the entire section below, including 'deny'
+ # 3. Add your local IPs to the 'allow' list
+ #- name: cors
+ #- name: ip-restriction
+ # config:
+ # allow:
+ # - 127.0.0.1
+ # - ::1
+ # deny: []
+
## Protected Dashboard - catch all remaining routes
- name: dashboard
_comment: 'Studio: /* -> http://studio:3000/*'
@@ -290,7 +468,7 @@ services:
config:
hide_credentials: true
supabase-studio:
- image: supabase/studio:2026.01.07-sha-037e5f9
+ image: supabase/studio:2026.03.16-sha-5528817
healthcheck:
test:
[
@@ -310,7 +488,11 @@ services:
- STUDIO_PG_META_URL=http://supabase-meta:8080
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- POSTGRES_HOST=${POSTGRES_HOST:-supabase-db}
- - CURRENT_CLI_VERSION=2.67.1
+ - POSTGRES_PORT=${POSTGRES_PORT:-5432}
+ - POSTGRES_DB=${POSTGRES_DB:-postgres}
+ - 'PGRST_DB_SCHEMAS=${PGRST_DB_SCHEMAS:-public,storage,graphql_public}'
+ - PGRST_DB_MAX_ROWS=${PGRST_DB_MAX_ROWS:-1000}
+ - PGRST_DB_EXTRA_SEARCH_PATH=${PGRST_DB_EXTRA_SEARCH_PATH:-public}
- DEFAULT_ORGANIZATION_NAME=${STUDIO_DEFAULT_ORGANIZATION:-Default Organization}
- DEFAULT_PROJECT_NAME=${STUDIO_DEFAULT_PROJECT:-Default Project}
@@ -320,10 +502,12 @@ services:
- SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}
- SUPABASE_SERVICE_KEY=${SERVICE_SUPABASESERVICE_KEY}
- AUTH_JWT_SECRET=${SERVICE_PASSWORD_JWT}
+ - PG_META_CRYPTO_KEY=${SERVICE_PASSWORD_PGMETACRYPTO}
- LOGFLARE_API_KEY=${SERVICE_PASSWORD_LOGFLARE}
+ - LOGFLARE_PUBLIC_ACCESS_TOKEN=${SERVICE_PASSWORD_LOGFLARE}
+ - LOGFLARE_PRIVATE_ACCESS_TOKEN=${SERVICE_PASSWORD_LOGFLAREPRIVATE}
- LOGFLARE_URL=http://supabase-analytics:4000
- - 'SUPABASE_PUBLIC_API=${SERVICE_URL_SUPABASEKONG}'
# Next.js client-side environment variables (required for browser access)
- 'NEXT_PUBLIC_SUPABASE_URL=${SERVICE_URL_SUPABASEKONG}'
- NEXT_PUBLIC_SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}
@@ -333,8 +517,13 @@ services:
# Uncomment to use Big Query backend for analytics
# NEXT_ANALYTICS_BACKEND_PROVIDER=bigquery
- 'OPENAI_API_KEY=${OPENAI_API_KEY}'
+ - SNIPPETS_MANAGEMENT_FOLDER=/app/snippets
+ - EDGE_FUNCTIONS_MANAGEMENT_FOLDER=/app/edge-functions
+ volumes:
+ - ./volumes/snippets:/app/snippets
+ - ./volumes/functions:/app/edge-functions
supabase-db:
- image: supabase/postgres:15.8.1.048
+ image: supabase/postgres:15.8.1.085
healthcheck:
test: pg_isready -U postgres -h 127.0.0.1
interval: 5s
@@ -365,7 +554,7 @@ services:
source: ./volumes/db/realtime.sql
target: /docker-entrypoint-initdb.d/migrations/99-realtime.sql
content: |
- \set pguser `echo "supabase_admin"`
+ \set pguser `echo "$POSTGRES_USER"`
create schema if not exists _realtime;
alter schema _realtime owner to :pguser;
@@ -380,7 +569,7 @@ services:
source: ./volumes/db/pooler.sql
target: /docker-entrypoint-initdb.d/migrations/99-pooler.sql
content: |
- \set pguser `echo "supabase_admin"`
+ \set pguser `echo "$POSTGRES_USER"`
\c _supabase
create schema if not exists _supavisor;
alter schema _supavisor owner to :pguser;
@@ -624,7 +813,7 @@ services:
source: ./volumes/db/logs.sql
target: /docker-entrypoint-initdb.d/migrations/99-logs.sql
content: |
- \set pguser `echo "supabase_admin"`
+ \set pguser `echo "$POSTGRES_USER"`
\c _supabase
create schema if not exists _analytics;
alter schema _analytics owner to :pguser;
@@ -633,7 +822,7 @@ services:
- supabase-db-config:/etc/postgresql-custom
supabase-analytics:
- image: supabase/logflare:1.4.0
+ image: supabase/logflare:1.31.2
healthcheck:
test: ["CMD", "curl", "http://127.0.0.1:4000/health"]
timeout: 5s
@@ -655,11 +844,10 @@ services:
- DB_PORT=${POSTGRES_PORT:-5432}
- DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
- DB_SCHEMA=_analytics
- - LOGFLARE_API_KEY=${SERVICE_PASSWORD_LOGFLARE}
+ - LOGFLARE_PUBLIC_ACCESS_TOKEN=${SERVICE_PASSWORD_LOGFLARE}
+ - LOGFLARE_PRIVATE_ACCESS_TOKEN=${SERVICE_PASSWORD_LOGFLAREPRIVATE}
- LOGFLARE_SINGLE_TENANT=true
- - LOGFLARE_SINGLE_TENANT_MODE=true
- LOGFLARE_SUPABASE_MODE=true
- - LOGFLARE_MIN_CLUSTER_SIZE=1
# Comment variables to use Big Query backend for analytics
- POSTGRES_BACKEND_URL=postgresql://supabase_admin:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/_supabase
@@ -670,7 +858,7 @@ services:
# GOOGLE_PROJECT_ID=${GOOGLE_PROJECT_ID}
# GOOGLE_PROJECT_NUMBER=${GOOGLE_PROJECT_NUMBER}
supabase-vector:
- image: timberio/vector:0.28.1-alpine
+ image: timberio/vector:0.53.0-alpine
healthcheck:
test:
[
@@ -722,13 +910,13 @@ services:
inputs:
- project_logs
route:
- kong: 'starts_with(string!(.appname), "supabase-kong")'
- auth: 'starts_with(string!(.appname), "supabase-auth")'
- rest: 'starts_with(string!(.appname), "supabase-rest")'
- realtime: 'starts_with(string!(.appname), "realtime-dev")'
- storage: 'starts_with(string!(.appname), "supabase-storage")'
- functions: 'starts_with(string!(.appname), "supabase-functions")'
- db: 'starts_with(string!(.appname), "supabase-db")'
+ kong: 'contains(string!(.appname), "supabase-kong")'
+ auth: 'contains(string!(.appname), "supabase-auth")'
+ rest: 'contains(string!(.appname), "supabase-rest")'
+ realtime: 'contains(string!(.appname), "supabase-realtime")'
+ storage: 'contains(string!(.appname), "supabase-storage")'
+ functions: 'contains(string!(.appname), "supabase-edge-functions")'
+ db: 'contains(string!(.appname), "supabase-db")'
# Ignores non nginx errors since they are related with kong booting up
kong_logs:
type: remap
@@ -741,10 +929,13 @@ services:
.metadata.request.headers.referer = req.referer
.metadata.request.headers.user_agent = req.agent
.metadata.request.headers.cf_connecting_ip = req.client
- .metadata.request.method = req.method
- .metadata.request.path = req.path
- .metadata.request.protocol = req.protocol
.metadata.response.status_code = req.status
+ url, split_err = split(req.request, " ")
+ if split_err == null {
+ .metadata.request.method = url[0]
+ .metadata.request.path = url[1]
+ .metadata.request.protocol = url[2]
+ }
}
if err != null {
abort
@@ -793,14 +984,20 @@ services:
parsed, err = parse_regex(.event_message, r'^(?P