diff --git a/app/pnpm-workspace.yaml b/app/pnpm-workspace.yaml new file mode 100644 index 00000000..c4ec45c0 --- /dev/null +++ b/app/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - . +allowBuilds: + esbuild: false diff --git a/app/src/components/Navigation.tsx b/app/src/components/Navigation.tsx index 47639224..adba7dd7 100644 --- a/app/src/components/Navigation.tsx +++ b/app/src/components/Navigation.tsx @@ -13,7 +13,9 @@ const Navigation = () => { "https://api.github.com/repos/dokploy/dokploy" ); const data = await response.json(); - setGithubStars(data.stargazers_count); + setGithubStars( + Number.isFinite(data.stargazers_count) ? data.stargazers_count : 0 + ); } catch (error) { console.error("Error fetching GitHub stars:", error); } diff --git a/blueprints/agent-zero/agent-zero.svg b/blueprints/agent-zero/agent-zero.svg new file mode 100644 index 00000000..1d2aea94 --- /dev/null +++ b/blueprints/agent-zero/agent-zero.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/agent-zero/docker-compose.yml b/blueprints/agent-zero/docker-compose.yml new file mode 100644 index 00000000..fceed3cf --- /dev/null +++ b/blueprints/agent-zero/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.8" +services: + agent-zero: + image: agent0ai/agent-zero:v1.9 + restart: unless-stopped + expose: + - "80" + environment: + - AUTH_LOGIN=${AUTH_LOGIN} + - AUTH_PASSWORD=${AUTH_PASSWORD} + volumes: + - agent-zero-data:/a0/usr +volumes: + agent-zero-data: \ No newline at end of file diff --git a/blueprints/agent-zero/template.toml b/blueprints/agent-zero/template.toml new file mode 100644 index 00000000..bb84517b --- /dev/null +++ b/blueprints/agent-zero/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +auth_login = "${username}" +auth_password = "${password:32}" + +[config] +env = [ + "AUTH_LOGIN=${auth_login}", + "AUTH_PASSWORD=${auth_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "agent-zero" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/blueprints/appwrite/docker-compose.yml b/blueprints/appwrite/docker-compose.yml index cb622284..2da23f52 100644 --- a/blueprints/appwrite/docker-compose.yml +++ b/blueprints/appwrite/docker-compose.yml @@ -8,12 +8,34 @@ x-logging: &x-logging max-size: "10m" services: appwrite: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 <<: *x-logging restart: unless-stopped labels: - traefik.enable=true - traefik.constraint-label-stack=appwrite + - 'traefik.http.routers.appwrite-sites-wildcard.rule=HostRegexp(`^.+\.${_APP_DOMAIN_SITES}$$`)' + - traefik.http.routers.appwrite-sites-wildcard.entrypoints=web + - traefik.http.routers.appwrite-sites-wildcard.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-sites-wildcard-secure.rule=HostRegexp(`^.+\.${_APP_DOMAIN_SITES}$$`)' + - traefik.http.routers.appwrite-sites-wildcard-secure.entrypoints=websecure + - traefik.http.routers.appwrite-sites-wildcard-secure.tls=true + - traefik.http.routers.appwrite-sites-wildcard-secure.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-functions-wildcard.rule=HostRegexp(`^.+\.${_APP_DOMAIN_FUNCTIONS}$$`)' + - traefik.http.routers.appwrite-functions-wildcard.entrypoints=web + - traefik.http.routers.appwrite-functions-wildcard.service=appwrite-wildcard + - 'traefik.http.routers.appwrite-functions-wildcard-secure.rule=HostRegexp(`^.+\.${_APP_DOMAIN_FUNCTIONS}$$`)' + - traefik.http.routers.appwrite-functions-wildcard-secure.entrypoints=websecure + - traefik.http.routers.appwrite-functions-wildcard-secure.tls=true + - traefik.http.routers.appwrite-functions-wildcard-secure.service=appwrite-wildcard + - traefik.http.services.appwrite-wildcard.loadbalancer.server.port=80 + healthcheck: + test: + - CMD + - doctor + interval: 5s + timeout: 5s + retries: 12 volumes: - appwrite-uploads:/storage/uploads:rw - appwrite-imports:/storage/imports:rw @@ -24,21 +46,25 @@ services: - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw depends_on: - - mariadb - redis - # - clamav + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_EDITION - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_LOCALE + - _APP_COMPRESSION_ENABLED - _APP_COMPRESSION_MIN_SIZE_BYTES - _APP_CONSOLE_WHITELIST_ROOT - _APP_CONSOLE_WHITELIST_EMAILS - _APP_CONSOLE_SESSION_ALERTS - _APP_CONSOLE_WHITELIST_IPS - _APP_CONSOLE_HOSTNAMES + - _APP_CONSOLE_SCHEMA - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_SYSTEM_TEAM_EMAIL - _APP_EMAIL_SECURITY - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE @@ -47,6 +73,8 @@ services: - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_CONSOLE_TRUSTED_PROJECTS - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A @@ -57,11 +85,18 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -108,8 +143,6 @@ services: - _APP_EXECUTOR_HOST - _APP_LOGGING_CONFIG - _APP_MAINTENANCE_INTERVAL - - _APP_MAINTENANCE_DELAY - - _APP_MAINTENANCE_START_TIME - _APP_MAINTENANCE_RETENTION_EXECUTION - _APP_MAINTENANCE_RETENTION_CACHE - _APP_MAINTENANCE_RETENTION_ABUSE @@ -119,6 +152,7 @@ services: - _APP_MAINTENANCE_RETENTION_SCHEDULES - _APP_SMS_PROVIDER - _APP_SMS_FROM + - _APP_GRAPHQL_INTROSPECTION - _APP_GRAPHQL_MAX_BATCH_SIZE - _APP_GRAPHQL_MAX_COMPLEXITY - _APP_GRAPHQL_MAX_DEPTH @@ -131,16 +165,26 @@ services: - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET - _APP_ASSISTANT_OPENAI_API_KEY + - _APP_CONSOLE_COUNTRIES_DENYLIST + - _APP_EXPERIMENT_LOGGING_PROVIDER + - _APP_EXPERIMENT_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + - _APP_DATABASE_SHARED_NAMESPACE + - _APP_FUNCTIONS_CREATION_ABUSE_LIMIT + - _APP_CUSTOM_DOMAIN_DENY_LIST + - _APP_TRUSTED_HEADERS + - _APP_MIGRATION_HOST + appwrite-console: <<: *x-logging - image: appwrite/console:7.4.7 + image: appwrite/console:8.7.5 restart: unless-stopped labels: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" appwrite-realtime: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: realtime <<: *x-logging restart: unless-stopped @@ -148,7 +192,7 @@ services: - "traefik.enable=true" - "traefik.constraint-label-stack=appwrite" depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV @@ -160,51 +204,69 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_USAGE_STATS - _APP_LOGGING_CONFIG + - _APP_LOGGING_CONFIG_REALTIME + - _APP_DATABASE_SHARED_TABLES + - _APP_POOL_ADAPTER=swoole appwrite-worker-audits: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-audits <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES appwrite-worker-webhooks: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-webhooks <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_EMAIL_SECURITY - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -215,15 +277,17 @@ services: - _APP_REDIS_USER - _APP_REDIS_PASS - _APP_LOGGING_CONFIG + - _APP_WEBHOOK_MAX_FAILED_ATTEMPTS + - _APP_DATABASE_SHARED_TABLES appwrite-worker-deletes: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-deletes <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-uploads:/storage/uploads:rw - appwrite-cache:/storage/cache:rw @@ -233,12 +297,16 @@ services: - appwrite-certificates:/storage/certificates:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -269,52 +337,64 @@ services: - _APP_LOGGING_CONFIG - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST - - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_DATABASE_SHARED_TABLES + - _APP_EMAIL_CERTIFICATES - _APP_MAINTENANCE_RETENTION_AUDIT - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - - _APP_MAINTENANCE_RETENTION_EXECUTION - - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - - _APP_EMAIL_CERTIFICATES appwrite-worker-databases: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-databases <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DB_ADAPTER_VECTORSDB + - _APP_DB_HOST_VECTORSDB + - _APP_DB_PORT_VECTORSDB + - _APP_DB_SCHEMA_VECTORSDB + - _APP_DB_USER_VECTORSDB + - _APP_DB_PASS_VECTORSDB - _APP_LOGGING_CONFIG + - _APP_QUEUE_NAME + - _APP_DATABASE_SHARED_TABLES appwrite-worker-builds: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-builds <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-functions:/storage/functions:rw - appwrite-sites:/storage/sites:rw - appwrite-builds:/storage/builds:rw - - appwrite-uploads:/storage/uploads:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_EXECUTOR_SECRET - _APP_EXECUTOR_HOST @@ -322,6 +402,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -336,10 +417,13 @@ services: - _APP_COMPUTE_BUILD_TIMEOUT - _APP_COMPUTE_CPUS - _APP_COMPUTE_MEMORY + - _APP_OPEN_RUNTIMES_NFT - _APP_COMPUTE_SIZE_LIMIT - _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_ROUTER_FORCE_HTTPS - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_CONSOLE_TRUSTED_PROJECTS - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -362,22 +446,85 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DATABASE_SHARED_TABLES - _APP_DOMAIN_SITES + extra_hosts: + - host.docker.internal:host-gateway + + appwrite-worker-screenshots: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-screenshots + <<: *x-logging + restart: unless-stopped + depends_on: + - redis + - ${_APP_DB_HOST:-mongodb} + volumes: + - appwrite-uploads:/storage/uploads:rw + environment: + - _APP_BROWSER_HOST + - _APP_WORKER_SCREENSHOTS_ROUTER + - _APP_OPTIONS_FORCE_HTTPS + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_LOGGING_CONFIG + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + extra_hosts: + - host.docker.internal:host-gateway appwrite-worker-certificates: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-certificates <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} volumes: - appwrite-config:/storage/config:rw - appwrite-certificates:/storage/certificates:rw environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME @@ -386,30 +533,64 @@ services: - _APP_DOMAIN_TARGET_CAA - _APP_DNS - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES - appwrite-worker-functions: - image: appwrite/appwrite:1.8.0 - entrypoint: worker-functions + appwrite-worker-executions: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-executions <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} + environment: + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-functions: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-functions + <<: *x-logging + restart: unless-stopped + depends_on: + - ${_APP_DB_HOST:-mongodb} + - redis - openruntimes-executor environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=8 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_OPTIONS_FORCE_HTTPS @@ -417,6 +598,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -433,9 +615,11 @@ services: - _APP_DOCKER_HUB_USERNAME - _APP_DOCKER_HUB_PASSWORD - _APP_LOGGING_CONFIG + - _APP_LOGGING_PROVIDER + - _APP_DATABASE_SHARED_TABLES appwrite-worker-mails: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-mails <<: *x-logging restart: unless-stopped @@ -443,19 +627,23 @@ services: - redis environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_SYSTEM_EMAIL_NAME - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_SMTP_HOST - _APP_SMTP_PORT - _APP_SMTP_SECURE @@ -464,9 +652,10 @@ services: - _APP_LOGGING_CONFIG - _APP_DOMAIN - _APP_OPTIONS_FORCE_HTTPS + - _APP_DATABASE_SHARED_TABLES appwrite-worker-messaging: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-messaging <<: *x-logging restart: unless-stopped @@ -476,12 +665,16 @@ services: - redis environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -490,6 +683,7 @@ services: - _APP_LOGGING_CONFIG - _APP_SMS_FROM - _APP_SMS_PROVIDER + - _APP_SMS_PROJECTS_DENY_LIST - _APP_STORAGE_DEVICE - _APP_STORAGE_S3_ACCESS_KEY - _APP_STORAGE_S3_SECRET @@ -512,19 +706,24 @@ services: - _APP_STORAGE_WASABI_SECRET - _APP_STORAGE_WASABI_REGION - _APP_STORAGE_WASABI_BUCKET + - _APP_DATABASE_SHARED_TABLES appwrite-worker-migrations: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-migrations <<: *x-logging restart: unless-stopped volumes: - appwrite-imports:/storage/imports:rw + - appwrite-uploads:/storage/uploads:rw depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME @@ -537,6 +736,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -545,17 +745,22 @@ services: - _APP_LOGGING_CONFIG - _APP_MIGRATIONS_FIREBASE_CLIENT_ID - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + - _APP_DATABASE_SHARED_TABLES + - _APP_OPTIONS_FORCE_HTTPS + - _APP_MIGRATION_HOST appwrite-task-maintenance: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: maintenance <<: *x-logging restart: unless-stopped depends_on: + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_DOMAIN - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA @@ -568,6 +773,7 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -581,19 +787,58 @@ services: - _APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_MAINTENANCE_START_TIME + - _APP_DATABASE_SHARED_TABLES + + appwrite-task-interval: + image: appwrite/appwrite:1.9.5 + entrypoint: interval + <<: *x-logging + restart: unless-stopped + depends_on: + - ${_APP_DB_HOST:-mongodb} + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_DOMAIN + - _APP_DOMAIN_TARGET_CNAME + - _APP_DOMAIN_TARGET_AAAA + - _APP_DOMAIN_TARGET_A + - _APP_DOMAIN_TARGET_CAA + - _APP_DNS + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES + - _APP_INTERVAL_DOMAIN_VERIFICATION + - _APP_INTERVAL_CLEANUP_STALE_EXECUTIONS appwrite-task-stats-resources: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: stats-resources <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -609,42 +854,21 @@ services: - _APP_STATS_RESOURCES_INTERVAL appwrite-worker-stats-resources: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: worker-stats-resources <<: *x-logging restart: unless-stopped depends_on: - redis - - mariadb - environment: - - _APP_ENV - - _APP_WORKER_PER_CORE - - _APP_OPENSSL_KEY_V1 - - _APP_DB_HOST - - _APP_DB_PORT - - _APP_DB_SCHEMA - - _APP_DB_USER - - _APP_DB_PASS - - _APP_REDIS_HOST - - _APP_REDIS_PORT - - _APP_REDIS_USER - - _APP_REDIS_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_CONFIG - - _APP_STATS_RESOURCES_INTERVAL - - appwrite-worker-stats-usage: - image: appwrite/appwrite:1.8.0 - entrypoint: worker-stats-usage - <<: *x-logging - restart: unless-stopped - depends_on: - - redis - - mariadb + - ${_APP_DB_HOST:-mongodb} environment: - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -657,91 +881,145 @@ services: - _APP_USAGE_STATS - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-stats-usage: + image: appwrite/appwrite:1.9.5 + entrypoint: worker-stats-usage + <<: *x-logging + restart: unless-stopped + depends_on: + - redis + - ${_APP_DB_HOST:-mongodb} + environment: + - _APP_ENV + - _APP_WORKERS_NUM=1 + - _APP_WORKER_MAX_COROUTINES=1 + - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER + - _APP_OPENSSL_KEY_V1 + - _APP_DB_ADAPTER + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-functions: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-functions <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_OPTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_MIGRATION_HOST + - _APP_CONSOLE_SCHEMA - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-executions: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-executions <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 + - _APP_OPTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_CONSOLE_DOMAIN + - _APP_DOMAIN_FUNCTIONS + - _APP_DOMAIN_SITES + - _APP_MIGRATION_HOST + - _APP_CONSOLE_SCHEMA - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-task-scheduler-messages: - image: appwrite/appwrite:1.8.0 + image: appwrite/appwrite:1.9.5 entrypoint: schedule-messages <<: *x-logging restart: unless-stopped depends_on: - - mariadb + - ${_APP_DB_HOST:-mongodb} - redis environment: - _APP_ENV - _APP_WORKER_PER_CORE + - _APP_POOL_ADAPTER - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS + - _APP_DB_ADAPTER - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS + - _APP_DATABASE_SHARED_TABLES appwrite-assistant: - image: appwrite/assistant:0.8.3 + image: appwrite/assistant:0.8.4 <<: *x-logging restart: unless-stopped environment: - _APP_ASSISTANT_OPENAI_API_KEY appwrite-browser: - image: appwrite/browser:0.2.4 + image: appwrite/browser:0.3.2 <<: *x-logging restart: unless-stopped openruntimes-executor: - hostname: exc1 + hostname: openruntimes-executor <<: *x-logging restart: unless-stopped stop_signal: SIGINT - image: openruntimes/executor:0.7.22 + image: openruntimes/executor:0.25.1 volumes: - /var/run/docker.sock:/var/run/docker.sock - appwrite-builds:/storage/builds:rw @@ -751,7 +1029,10 @@ services: # It's not possible to share mount file between 2 containers without host mount (copying is too slow) - /tmp:/tmp:rw environment: - - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD + - OPR_EXECUTOR_CONNECTION_STORAGE=local://localhost + - OPR_EXECUTOR_CONNECTION_BUILD_CACHE_STORAGE= + - OPR_EXECUTOR_IMAGES=$_APP_EXECUTOR_IMAGES + - OPR_EXECUTOR_INACTIVE_THRESHOLD=$_APP_COMPUTE_INACTIVE_THRESHOLD - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_COMPUTE_MAINTENANCE_INTERVAL - OPR_EXECUTOR_NETWORK=$_APP_COMPUTE_RUNTIMES_NETWORK - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME @@ -760,6 +1041,7 @@ services: - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET - OPR_EXECUTOR_RUNTIME_VERSIONS=v5 + - OPEN_RUNTIMES_NFT=$_APP_OPEN_RUNTIMES_NFT - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY @@ -783,6 +1065,55 @@ services: - OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET - OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET + healthcheck: + test: + - CMD-SHELL + - 'curl -fsS -H "Authorization: Bearer $$OPR_EXECUTOR_SECRET" http://localhost/v1/health >/dev/null' + interval: 5s + timeout: 3s + retries: 20 + start_period: 5s + + mongodb: + image: mongo:8.2.5 + <<: *x-logging + restart: unless-stopped + volumes: + - appwrite-mongodb:/data/db + - appwrite-mongodb-keyfile:/data/keyfile + - ../files/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro + - ../files/mongo-entrypoint.sh:/mongo-entrypoint.sh:ro + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} + - MONGO_INITDB_DATABASE=${_APP_DB_SCHEMA} + - MONGO_INITDB_USERNAME=${_APP_DB_USER} + - MONGO_INITDB_PASSWORD=${_APP_DB_PASS} + entrypoint: + - /bin/bash + - /mongo-entrypoint.sh + healthcheck: + test: | + bash -c " + mongosh -u root -p \"$$MONGO_INITDB_ROOT_PASSWORD\" --authenticationDatabase admin --quiet --eval \" + try { + const hello = db.adminCommand({hello: 1}); + if (hello.isWritablePrimary) { + quit(0); + } + } catch (e) {} + + try { + rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'mongodb:27017'}]}); + } catch (e) {} + + quit(1); + \" 2>/dev/null + " + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s mariadb: image: mariadb:10.11 # fix issues when upgrading using: mysql_upgrade -u root -p @@ -799,7 +1130,7 @@ services: command: "mysqld --innodb-flush-method=fsync" redis: - image: redis:7.2.4-alpine + image: redis:7.4.7-alpine <<: *x-logging restart: unless-stopped command: > @@ -818,6 +1149,8 @@ services: volumes: appwrite-mariadb: + appwrite-mongodb: + appwrite-mongodb-keyfile: appwrite-redis: appwrite-cache: appwrite-uploads: diff --git a/blueprints/appwrite/template.toml b/blueprints/appwrite/template.toml index cca2d43d..54d3a8bc 100644 --- a/blueprints/appwrite/template.toml +++ b/blueprints/appwrite/template.toml @@ -10,58 +10,69 @@ executor_secret = "${password:32}" [config] env = [ "_APP_ENV=production", + "_APP_EDITION=self-hosted", "_APP_LOCALE=en", + "_APP_POOL_ADAPTER=stack", + "_APP_WORKER_PER_CORE=6", "_APP_OPTIONS_ABUSE=enabled", "_APP_OPTIONS_FORCE_HTTPS=disabled", - "_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled", "_APP_OPTIONS_ROUTER_PROTECTION=disabled", "_APP_OPENSSL_KEY_V1=${openssl_key}", "_APP_DOMAIN=${main_domain}", + "_APP_CONSOLE_DOMAIN=${main_domain}", + "_APP_CONSOLE_TRUSTED_PROJECTS=", "_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com", "_APP_DOMAIN_FUNCTIONS=${functions_domain}", "_APP_DOMAIN_SITES=${sites_domain}", - "_APP_DOMAIN_TARGET=localhost", "_APP_DOMAIN_TARGET_CNAME=localhost", "_APP_DOMAIN_TARGET_AAAA=::1", "_APP_DOMAIN_TARGET_A=127.0.0.1", "_APP_DOMAIN_TARGET_CAA=", "_APP_DNS=8.8.8.8", + "_APP_TRUSTED_HEADERS=x-forwarded-for", "_APP_CONSOLE_WHITELIST_ROOT=enabled", "_APP_CONSOLE_WHITELIST_EMAILS=", "_APP_CONSOLE_WHITELIST_IPS=", "_APP_CONSOLE_HOSTNAMES=", + "_APP_CONSOLE_SCHEMA=appwriteio", + "_APP_CONSOLE_SESSION_ALERTS=disabled", + "_APP_CONSOLE_COUNTRIES_DENYLIST=", "_APP_SYSTEM_EMAIL_NAME=Appwrite", "_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io", "_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io", "_APP_SYSTEM_RESPONSE_FORMAT=", - "_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io", "_APP_EMAIL_SECURITY=", "_APP_EMAIL_CERTIFICATES=", - "_APP_USAGE_STATS=enabled", - "_APP_LOGGING_PROVIDER=", - "_APP_LOGGING_CONFIG=", - "_APP_USAGE_AGGREGATION_INTERVAL=30", - "_APP_USAGE_TIMESERIES_INTERVAL=30", - "_APP_USAGE_DATABASE_INTERVAL=900", - "_APP_WORKER_PER_CORE=6", - "_APP_CONSOLE_SESSION_ALERTS=disabled", "_APP_COMPRESSION_ENABLED=enabled", "_APP_COMPRESSION_MIN_SIZE_BYTES=1024", + "_APP_USAGE_STATS=enabled", + "_APP_USAGE_AGGREGATION_INTERVAL=30", + "_APP_STATS_RESOURCES_INTERVAL=30", + "_APP_LOGGING_PROVIDER=", + "_APP_LOGGING_CONFIG=", + "_APP_LOGGING_CONFIG_REALTIME=", + "_APP_EXPERIMENT_LOGGING_PROVIDER=", + "_APP_EXPERIMENT_LOGGING_CONFIG=", "_APP_REDIS_HOST=redis", "_APP_REDIS_PORT=6379", "_APP_REDIS_USER=", "_APP_REDIS_PASS=", - "_APP_DB_HOST=mariadb", - "_APP_DB_PORT=3306", + "_APP_DB_ADAPTER=mongodb", + "_APP_DB_HOST=mongodb", + "_APP_DB_PORT=27017", "_APP_DB_SCHEMA=appwrite", "_APP_DB_USER=user", "_APP_DB_PASS=${db_user_pw}", "_APP_DB_ROOT_PASS=${db_root_pw}", - "_APP_INFLUXDB_HOST=influxdb", - "_APP_INFLUXDB_PORT=8086", - "_APP_STATSD_HOST=telegraf", - "_APP_STATSD_PORT=8125", + "_APP_DATABASE_SHARED_TABLES=", + "_APP_DATABASE_SHARED_NAMESPACE=", + "_APP_DB_ADAPTER_VECTORSDB=", + "_APP_DB_HOST_VECTORSDB=", + "_APP_DB_PORT_VECTORSDB=", + "_APP_DB_SCHEMA_VECTORSDB=", + "_APP_DB_USER_VECTORSDB=", + "_APP_DB_PASS_VECTORSDB=", "_APP_SMTP_HOST=", "_APP_SMTP_PORT=", "_APP_SMTP_SECURE=", @@ -69,6 +80,7 @@ env = [ "_APP_SMTP_PASSWORD=", "_APP_SMS_PROVIDER=", "_APP_SMS_FROM=", + "_APP_SMS_PROJECTS_DENY_LIST=", "_APP_STORAGE_LIMIT=30000000", "_APP_STORAGE_PREVIEW_LIMIT=20000000", "_APP_STORAGE_ANTIVIRUS=disabled", @@ -96,45 +108,27 @@ env = [ "_APP_STORAGE_WASABI_SECRET=", "_APP_STORAGE_WASABI_REGION=eu-central-1", "_APP_STORAGE_WASABI_BUCKET=", - "_APP_FUNCTIONS_SIZE_LIMIT=30000000", "_APP_COMPUTE_SIZE_LIMIT=30000000", - "_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000", - "_APP_FUNCTIONS_TIMEOUT=900", - "_APP_FUNCTIONS_BUILD_TIMEOUT=900", "_APP_COMPUTE_BUILD_TIMEOUT=900", - "_APP_FUNCTIONS_CONTAINERS=10", - "_APP_FUNCTIONS_CPUS=0", "_APP_COMPUTE_CPUS=0", - "_APP_FUNCTIONS_MEMORY=0", "_APP_COMPUTE_MEMORY=0", - "_APP_FUNCTIONS_MEMORY_SWAP=0", - "_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0", - "_APP_EXECUTOR_SECRET=${executor_secret}", - "_APP_EXECUTOR_HOST=http://exc1/v1", - "_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0", - "_APP_FUNCTIONS_INACTIVE_THRESHOLD=60", "_APP_COMPUTE_INACTIVE_THRESHOLD=60", - "DOCKERHUB_PULL_USERNAME=", - "DOCKERHUB_PULL_PASSWORD=", - "DOCKERHUB_PULL_EMAIL=", - "OPEN_RUNTIMES_NETWORK=appwrite_runtimes", - "_APP_FUNCTIONS_RUNTIMES_NETWORK=dokploy-network", + "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", "_APP_COMPUTE_RUNTIMES_NETWORK=dokploy-network", + "_APP_FUNCTIONS_TIMEOUT=900", + "_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000", + "_APP_FUNCTIONS_RUNTIMES=node-22,python-3.12,php-8.3,ruby-3.3,dart-3.5,go-1.23,rust-1.83,bun-1.1,deno-1.46,java-21.0,dotnet-8.0", + "_APP_SITES_TIMEOUT=900", + "_APP_SITES_RUNTIMES=static-1,node-22", + "_APP_EXECUTOR_SECRET=${executor_secret}", + "_APP_EXECUTOR_HOST=http://openruntimes-executor/v1", + "_APP_EXECUTOR_IMAGES=openruntimes/node:v5-22,openruntimes/python:v5-3.12,openruntimes/php:v5-8.3,openruntimes/static:v5-1", + "_APP_OPEN_RUNTIMES_NFT=enabled", + "_APP_BROWSER_HOST=http://appwrite-browser:3000/v1", + "_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite", "_APP_DOCKER_HUB_USERNAME=", "_APP_DOCKER_HUB_PASSWORD=", - "_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600", - "_APP_COMPUTE_MAINTENANCE_INTERVAL=3600", - "_APP_SITES_TIMEOUT=900", - "_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29", - "_APP_VCS_GITHUB_APP_NAME=", - "_APP_VCS_GITHUB_PRIVATE_KEY=", - "_APP_VCS_GITHUB_APP_ID=", - "_APP_VCS_GITHUB_CLIENT_ID=", - "_APP_VCS_GITHUB_CLIENT_SECRET=", - "_APP_VCS_GITHUB_WEBHOOK_SECRET=", "_APP_MAINTENANCE_INTERVAL=86400", - "_APP_MAINTENANCE_DELAY=0", "_APP_MAINTENANCE_START_TIME=00:00", "_APP_MAINTENANCE_RETENTION_CACHE=2592000", "_APP_MAINTENANCE_RETENTION_EXECUTION=1209600", @@ -143,14 +137,67 @@ env = [ "_APP_MAINTENANCE_RETENTION_ABUSE=86400", "_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000", "_APP_MAINTENANCE_RETENTION_SCHEDULES=86400", + "_APP_INTERVAL_DOMAIN_VERIFICATION=120", + "_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300", + "_APP_GRAPHQL_INTROSPECTION=enabled", "_APP_GRAPHQL_MAX_BATCH_SIZE=10", "_APP_GRAPHQL_MAX_COMPLEXITY=250", - "_APP_GRAPHQL_MAX_DEPTH=3", + "_APP_GRAPHQL_MAX_DEPTH=4", + "_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10", + "_APP_VCS_GITHUB_APP_NAME=", + "_APP_VCS_GITHUB_PRIVATE_KEY=", + "_APP_VCS_GITHUB_APP_ID=", + "_APP_VCS_GITHUB_CLIENT_ID=", + "_APP_VCS_GITHUB_CLIENT_SECRET=", + "_APP_VCS_GITHUB_WEBHOOK_SECRET=", + "_APP_MIGRATION_HOST=appwrite", "_APP_MIGRATIONS_FIREBASE_CLIENT_ID=", "_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=", "_APP_ASSISTANT_OPENAI_API_KEY=", ] -mounts = [] + +[[config.mounts]] +filePath = "mongo-init.js" +content = """// mongo-init.js + +// Switch to the admin database +const adminDb = db.getSiblingDB('admin'); + +// Get username and password from environment variables +const username = process.env.MONGO_INITDB_USERNAME; +const password = process.env.MONGO_INITDB_PASSWORD; +const database = process.env.MONGO_INITDB_DATABASE; + +// Create the user +adminDb.createUser({ + user: username, + pwd: password, + roles: [ + { role: 'readWrite', db: database } + ] +}); +""" + +[[config.mounts]] +filePath = "mongo-entrypoint.sh" +content = """#!/bin/bash +set -e + +# Fix keyfile permissions if mounted from volume +KEYFILE_PATH="/data/keyfile/mongo-keyfile" + +if [ ! -f "$KEYFILE_PATH" ]; then + echo "Generating random MongoDB keyfile..." + mkdir -p /data/keyfile + openssl rand -base64 756 > "$KEYFILE_PATH" +fi + +chmod 400 "$KEYFILE_PATH" +chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH" + +# Use MongoDB's standard entrypoint with our command +exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH" +""" [[config.domains]] serviceName = "appwrite" diff --git a/blueprints/arche/arche.svg b/blueprints/arche/arche.svg new file mode 100644 index 00000000..06ae9720 --- /dev/null +++ b/blueprints/arche/arche.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/arche/docker-compose.yml b/blueprints/arche/docker-compose.yml new file mode 100644 index 00000000..cbefbe7e --- /dev/null +++ b/blueprints/arche/docker-compose.yml @@ -0,0 +1,102 @@ +version: "3.8" + +# Arche — AI workspace platform that spawns per-user OpenCode containers +# via a Docker socket proxy. +# +# PRE-REQUISITES (run once on the VPS before deploying): +# mkdir -p /opt/arche/kb-content /opt/arche/kb-config /opt/arche/users +# +# Optional (only needed to seed the Knowledge Base): +# git init --bare --initial-branch=main /opt/arche/kb-content +# git init --bare --initial-branch=main /opt/arche/kb-config +# +# POST-DEPLOY (set in Dokploy → Environment, then redeploy): +# 1. Deploy this template once. Dokploy auto-creates a network for the +# compose stack. +# 2. SSH into the host and find the network name: +# docker network ls --format '{{.Name}}' | grep arche +# It looks like `_default` or similar. +# 3. Add the env var OPENCODE_NETWORK= and redeploy. +# Without it, spawned user workspaces will fail to start. +# +# IMAGE ARCHITECTURE: +# Default tags target linux/amd64. For ARM64 hosts (Apple Silicon, +# Ampere, Graviton), override both images to the `-arm64` variants in +# Dokploy → Environment: +# ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest-arm64 +# ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest-arm64 + +services: + web: + image: ${ARCHE_WEB_IMAGE:-ghcr.io/peaberry-studio/arche/web:latest} + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + docker-socket-proxy: + condition: service_started + environment: + ARCHE_DOMAIN: ${ARCHE_DOMAIN} + DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/arche?schema=public + + ARCHE_SESSION_PEPPER: ${ARCHE_SESSION_PEPPER} + ARCHE_ENCRYPTION_KEY: ${ARCHE_ENCRYPTION_KEY} + ARCHE_INTERNAL_TOKEN: ${ARCHE_INTERNAL_TOKEN} + ARCHE_GATEWAY_TOKEN_SECRET: ${ARCHE_GATEWAY_TOKEN_SECRET} + ARCHE_CONNECTOR_OAUTH_STATE_SECRET: ${ARCHE_CONNECTOR_OAUTH_STATE_SECRET} + + ARCHE_GATEWAY_TOKEN_TTL_SECONDS: "86400" + ARCHE_GATEWAY_BASE_URL: http://web:3000 + ARCHE_PUBLIC_BASE_URL: https://${ARCHE_DOMAIN} + ARCHE_SESSION_TTL_DAYS: "7" + ARCHE_COOKIE_SECURE: "true" + + ARCHE_SEED_ADMIN_EMAIL: ${ARCHE_SEED_ADMIN_EMAIL} + ARCHE_SEED_ADMIN_PASSWORD: ${ARCHE_SEED_ADMIN_PASSWORD} + ARCHE_SEED_ADMIN_SLUG: ${ARCHE_SEED_ADMIN_SLUG:-admin} + + CONTAINER_PROXY_HOST: docker-socket-proxy + CONTAINER_PROXY_PORT: "2375" + OPENCODE_IMAGE: ${ARCHE_WORKSPACE_IMAGE:-ghcr.io/peaberry-studio/arche/workspace:latest} + OPENCODE_NETWORK: ${OPENCODE_NETWORK} + + KB_CONTENT_HOST_PATH: /opt/arche/kb-content + KB_CONFIG_HOST_PATH: /opt/arche/kb-config + ARCHE_USERS_PATH: /opt/arche/users + expose: + - "3000" + volumes: + - /opt/arche/kb-content:/kb-content + - /opt/arche/kb-config:/kb-config + - /opt/arche/users:/opt/arche/users + + postgres: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: arche + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 10s + timeout: 5s + retries: 5 + + docker-socket-proxy: + image: ghcr.io/tecnativa/docker-socket-proxy:master + restart: unless-stopped + environment: + CONTAINERS: 1 + NETWORKS: 1 + IMAGES: 1 + INFO: 1 + POST: 1 + VOLUMES: 1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + +volumes: + postgres_data: diff --git a/blueprints/arche/template.toml b/blueprints/arche/template.toml new file mode 100644 index 00000000..5772c7b3 --- /dev/null +++ b/blueprints/arche/template.toml @@ -0,0 +1,33 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +session_pepper = "${base64:32}" +encryption_key = "${base64:32}" +internal_token = "${base64:32}" +gateway_token_secret = "${base64:32}" +connector_oauth_state_secret = "${base64:32}" +seed_admin_email = "${email}" +seed_admin_password = "${password:16}" + +[config] +env = [ + "ARCHE_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", + "ARCHE_SESSION_PEPPER=${session_pepper}", + "ARCHE_ENCRYPTION_KEY=${encryption_key}", + "ARCHE_INTERNAL_TOKEN=${internal_token}", + "ARCHE_GATEWAY_TOKEN_SECRET=${gateway_token_secret}", + "ARCHE_CONNECTOR_OAUTH_STATE_SECRET=${connector_oauth_state_secret}", + "ARCHE_SEED_ADMIN_EMAIL=${seed_admin_email}", + "ARCHE_SEED_ADMIN_PASSWORD=${seed_admin_password}", + "ARCHE_SEED_ADMIN_SLUG=admin", + "ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest", + "ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest", + "OPENCODE_NETWORK=", +] +mounts = [] + +[[config.domains]] +serviceName = "web" +port = 3000 +host = "${main_domain}" diff --git a/blueprints/autobase/docker-compose.yml b/blueprints/autobase/docker-compose.yml index 13b23d57..7582c9c6 100644 --- a/blueprints/autobase/docker-compose.yml +++ b/blueprints/autobase/docker-compose.yml @@ -1,6 +1,6 @@ services: autobase-console: - image: autobase/console:2.5.2 + image: autobase/console:2.7.2 restart: unless-stopped ports: - "80" @@ -10,8 +10,10 @@ services: - PG_CONSOLE_AUTHORIZATION_TOKEN=${PG_CONSOLE_AUTHORIZATION_TOKEN} volumes: - console_postgres:/var/lib/postgresql + - dbdesk_studio_data:/opt/dbdesk-studio - /var/run/docker.sock:/var/run/docker.sock - /tmp/ansible:/tmp/ansible volumes: console_postgres: + dbdesk_studio_data: diff --git a/blueprints/billmora/billmora.svg b/blueprints/billmora/billmora.svg new file mode 100644 index 00000000..e9daa0b3 --- /dev/null +++ b/blueprints/billmora/billmora.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/blueprints/billmora/docker-compose.yml b/blueprints/billmora/docker-compose.yml new file mode 100644 index 00000000..870629e1 --- /dev/null +++ b/blueprints/billmora/docker-compose.yml @@ -0,0 +1,130 @@ +version: "3.8" + +services: + billmora-app: + image: ghcr.io/billmora/billmora:latest + restart: always + environment: + - APP_NAME=${APP_NAME} + - APP_ENV=${APP_ENV} + - APP_KEY=${APP_KEY} + - APP_URL=${APP_URL} + - DB_CONNECTION=${DB_CONNECTION} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - REDIS_HOST=${REDIS_HOST} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_PORT=${REDIS_PORT} + - SESSION_DRIVER=${SESSION_DRIVER} + - CACHE_STORE=${CACHE_STORE} + - QUEUE_CONNECTION=${QUEUE_CONNECTION} + - AUTORUN_ENABLED=true + - AUTORUN_LARAVEL_MIGRATION=true + - AUTORUN_LARAVEL_MIGRATION_FORCE=true + - AUTORUN_LARAVEL_MIGRATION_SEED=true + - AUTORUN_LARAVEL_STORAGE_LINK=true + volumes: + - billmora-storage:/var/www/html/storage + depends_on: + billmora-db: + condition: service_healthy + billmora-redis: + condition: service_healthy + + billmora-worker: + image: ghcr.io/billmora/billmora:latest + restart: always + command: ["php", "artisan", "queue:work", "--sleep=3", "--tries=3", "--max-time=3600"] + environment: + - APP_NAME=${APP_NAME} + - APP_ENV=${APP_ENV} + - APP_KEY=${APP_KEY} + - APP_URL=${APP_URL} + - DB_CONNECTION=${DB_CONNECTION} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - REDIS_HOST=${REDIS_HOST} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_PORT=${REDIS_PORT} + - SESSION_DRIVER=${SESSION_DRIVER} + - CACHE_STORE=${CACHE_STORE} + - QUEUE_CONNECTION=${QUEUE_CONNECTION} + - AUTORUN_ENABLED=false + volumes: + - billmora-storage:/var/www/html/storage + depends_on: + billmora-db: + condition: service_healthy + billmora-redis: + condition: service_healthy + + billmora-scheduler: + image: ghcr.io/billmora/billmora:latest + restart: always + command: ["/bin/sh", "-c", "while true; do php artisan schedule:run >> /dev/null 2>&1; sleep 60; done"] + environment: + - APP_NAME=${APP_NAME} + - APP_ENV=${APP_ENV} + - APP_KEY=${APP_KEY} + - APP_URL=${APP_URL} + - DB_CONNECTION=${DB_CONNECTION} + - DB_HOST=${DB_HOST} + - DB_PORT=${DB_PORT} + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - REDIS_HOST=${REDIS_HOST} + - REDIS_PASSWORD=${REDIS_PASSWORD} + - REDIS_PORT=${REDIS_PORT} + - SESSION_DRIVER=${SESSION_DRIVER} + - CACHE_STORE=${CACHE_STORE} + - QUEUE_CONNECTION=${QUEUE_CONNECTION} + - AUTORUN_ENABLED=false + volumes: + - billmora-storage:/var/www/html/storage + depends_on: + billmora-db: + condition: service_healthy + billmora-redis: + condition: service_healthy + + billmora-db: + image: mariadb:11 + restart: always + environment: + - MARIADB_DATABASE=${DB_DATABASE} + - MARIADB_USER=${DB_USERNAME} + - MARIADB_PASSWORD=${DB_PASSWORD} + - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + volumes: + - billmora-db-data:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + + billmora-redis: + image: redis:7-alpine + restart: always + command: redis-server --requirepass ${REDIS_PASSWORD} + volumes: + - billmora-redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "${REDIS_PASSWORD}", "ping"] + interval: 10s + timeout: 3s + retries: 5 + start_period: 10s + +volumes: + billmora-storage: + billmora-db-data: + billmora-redis-data: diff --git a/blueprints/billmora/template.toml b/blueprints/billmora/template.toml new file mode 100644 index 00000000..40a37e29 --- /dev/null +++ b/blueprints/billmora/template.toml @@ -0,0 +1,31 @@ +[variables] +APP_KEY = "base64:${base64:32}" +APP_URL = "https://${domain}" +DB_PASSWORD = "${password:16}" +DB_ROOT_PASSWORD = "${password:16}" +REDIS_PASSWORD = "${password:16}" + +[config] +[[config.domains]] +serviceName = "billmora-app" +port = 8080 +host = "${domain}" + +[config.env] +APP_NAME = "Billmora" +APP_ENV = "production" +APP_KEY = "${APP_KEY}" +APP_URL = "${APP_URL}" +DB_CONNECTION = "mariadb" +DB_HOST = "billmora-db" +DB_PORT = "3306" +DB_DATABASE = "billmora" +DB_USERNAME = "billmora" +DB_PASSWORD = "${DB_PASSWORD}" +DB_ROOT_PASSWORD = "${DB_ROOT_PASSWORD}" +REDIS_HOST = "billmora-redis" +REDIS_PASSWORD = "${REDIS_PASSWORD}" +REDIS_PORT = "6379" +SESSION_DRIVER = "redis" +CACHE_STORE = "redis" +QUEUE_CONNECTION = "redis" diff --git a/blueprints/crawlab-master/crawlab.jpeg b/blueprints/crawlab-master/crawlab.jpeg new file mode 100644 index 00000000..1aef3cb9 Binary files /dev/null and b/blueprints/crawlab-master/crawlab.jpeg differ diff --git a/blueprints/crawlab-master/docker-compose.yml b/blueprints/crawlab-master/docker-compose.yml new file mode 100644 index 00000000..8b4f58af --- /dev/null +++ b/blueprints/crawlab-master/docker-compose.yml @@ -0,0 +1,41 @@ +version: "3.8" +services: + crawlab-master: + image: crawlabteam/crawlab:latest + restart: unless-stopped + environment: + - CRAWLAB_NODE_MASTER=Y + - CRAWLAB_MONGO_HOST=mongo + - CRAWLAB_MONGO_PORT=27017 + - CRAWLAB_MONGO_DB=crawlab + - CRAWLAB_MONGO_USERNAME=${MONGO_USERNAME} + - CRAWLAB_MONGO_PASSWORD=${MONGO_PASSWORD} + - CRAWLAB_MONGO_AUTHSOURCE=admin + volumes: + - crawlab-master-data:/root/.crawlab + - crawlab-app-data:/data + - crawlab-logs:/var/log/crawlab + expose: + - "8080" + - "9666" + depends_on: + - mongo + deploy: + resources: + limits: + cpus: "${CPU_LIMIT:-0.8}" # 0.8 will limit to 80% of CPU if one core. If 2 cores, you need to put 0.8*2=1.6 + + mongo: + image: mongo:4.2 + restart: unless-stopped + environment: + - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD} + volumes: + - mongo-data:/data/db + +volumes: + crawlab-master-data: + crawlab-app-data: + crawlab-logs: + mongo-data: diff --git a/blueprints/crawlab-master/template.toml b/blueprints/crawlab-master/template.toml new file mode 100644 index 00000000..e1bf5524 --- /dev/null +++ b/blueprints/crawlab-master/template.toml @@ -0,0 +1,12 @@ +[variables] +MONGO_USERNAME = "crawlab" +MONGO_PASSWORD = "${password:24}" +CPU_LIMIT = "0.8" + +[config] +env = ["MONGO_USERNAME=${MONGO_USERNAME}", "MONGO_PASSWORD=${MONGO_PASSWORD}", "CPU_LIMIT=${CPU_LIMIT}"] + +[[config.domains]] +serviceName = "crawlab-master" +port = 8080 +host = "${domain}" diff --git a/blueprints/crawlab-worker/crawlab.jpeg b/blueprints/crawlab-worker/crawlab.jpeg new file mode 100644 index 00000000..1aef3cb9 Binary files /dev/null and b/blueprints/crawlab-worker/crawlab.jpeg differ diff --git a/blueprints/crawlab-worker/docker-compose.yml b/blueprints/crawlab-worker/docker-compose.yml new file mode 100644 index 00000000..7f147afa --- /dev/null +++ b/blueprints/crawlab-worker/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" +services: + crawlab-worker: + image: crawlabteam/crawlab:latest + restart: unless-stopped + environment: + - CRAWLAB_NODE_MASTER=N + - CRAWLAB_GRPC_ADDRESS=${MASTER_GRPC_ADDRESS} + - CRAWLAB_FS_FILER_URL=${MASTER_FILER_URL} + volumes: + - crawlab-worker-data:/root/.crawlab + - crawlab-worker-app:/data + deploy: + resources: + limits: + cpus: "${CPU_LIMIT:-0.8}" # 0.8 will limit to 80% of CPU if one core. If 2 cores, you need to put 0.8*2=1.6 + +volumes: + crawlab-worker-data: + crawlab-worker-app: diff --git a/blueprints/crawlab-worker/template.toml b/blueprints/crawlab-worker/template.toml new file mode 100644 index 00000000..ba9e53d1 --- /dev/null +++ b/blueprints/crawlab-worker/template.toml @@ -0,0 +1,7 @@ +[variables] +MASTER_GRPC_ADDRESS = ":9666" +MASTER_FILER_URL = "http://:8080/api/filer" +CPU_LIMIT = "0.8" + +[config] +env = ["MASTER_GRPC_ADDRESS=${MASTER_GRPC_ADDRESS}", "MASTER_FILER_URL=${MASTER_FILER_URL}", "CPU_LIMIT=${CPU_LIMIT}"] diff --git a/blueprints/cut/cut.svg b/blueprints/cut/cut.svg new file mode 100644 index 00000000..c0604eac --- /dev/null +++ b/blueprints/cut/cut.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/blueprints/cut/docker-compose.yml b/blueprints/cut/docker-compose.yml new file mode 100644 index 00000000..b31578d1 --- /dev/null +++ b/blueprints/cut/docker-compose.yml @@ -0,0 +1,37 @@ +services: + cut: + image: ghcr.io/mendylanda/cut:latest + restart: unless-stopped + depends_on: + redis: + condition: service_healthy + environment: + # Injected by Dokploy from template.toml (generated strong password). + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + # Bundled Redis below, private to the project network. + - REDIS_URL=redis://redis:6379 + # Expose the container port only; Dokploy maps the domain to it. + ports: + - 3000 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/"] + interval: 10s + timeout: 5s + retries: 10 + + redis: + image: redis:7-alpine + restart: unless-stopped + # appendonly = durable across restarts; noeviction because this is Cut's + # primary datastore, not a cache (don't drop links under memory pressure). + command: redis-server --appendonly yes --maxmemory-policy noeviction + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 10 + +volumes: + redis-data: diff --git a/blueprints/cut/template.toml b/blueprints/cut/template.toml new file mode 100644 index 00000000..83de686c --- /dev/null +++ b/blueprints/cut/template.toml @@ -0,0 +1,14 @@ +[variables] +main_domain = "${domain}" +admin_password = "${password:24}" + +[config] +env = [ + "ADMIN_PASSWORD=${admin_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "cut" +port = 3000 +host = "${main_domain}" diff --git a/blueprints/discourse/docker-compose.yml b/blueprints/discourse/docker-compose.yml index caafab5b..d0353e8a 100644 --- a/blueprints/discourse/docker-compose.yml +++ b/blueprints/discourse/docker-compose.yml @@ -1,29 +1,24 @@ -version: '3.7' - services: discourse-db: - image: docker.io/bitnami/postgresql:17 - + image: pgvector/pgvector:pg16 volumes: - - discourse-postgresql-data:/bitnami/postgresql + - discourse-postgresql-data:/var/lib/postgresql/data environment: - POSTGRESQL_USERNAME: bn_discourse - POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRESQL_DATABASE: bitnami_discourse + POSTGRES_USER: discourse + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: discourse healthcheck: - test: ["CMD-SHELL", "pg_isready -U bn_discourse -d bitnami_discourse"] + test: ["CMD-SHELL", "pg_isready -U discourse -d discourse"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped discourse-redis: - image: docker.io/bitnami/redis:7.4 - + image: redis:7-alpine volumes: - - discourse-redis-data:/bitnami/redis - environment: - REDIS_PASSWORD: ${REDIS_PASSWORD} + - discourse-redis-data:/data + command: redis-server --requirepass ${REDIS_PASSWORD} healthcheck: test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] interval: 10s @@ -32,59 +27,38 @@ services: restart: unless-stopped discourse-app: - image: docker.io/bitnamilegacy/discourse:3.5.0 - + image: discourse/discourse:2026.1.4 volumes: - - discourse-data:/bitnami/discourse + - discourse-data:/shared depends_on: discourse-db: condition: service_healthy discourse-redis: condition: service_healthy environment: - DISCOURSE_HOST: ${DISCOURSE_HOST} - DISCOURSE_DATABASE_HOST: discourse-db - DISCOURSE_DATABASE_PORT_NUMBER: 5432 - DISCOURSE_DATABASE_USER: bn_discourse - DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD} - DISCOURSE_DATABASE_NAME: bitnami_discourse + DISCOURSE_HOSTNAME: ${DISCOURSE_HOST} + DISCOURSE_DB_HOST: discourse-db + DISCOURSE_DB_PORT: 5432 + DISCOURSE_DB_USERNAME: discourse + DISCOURSE_DB_PASSWORD: ${POSTGRES_PASSWORD} + DISCOURSE_DB_NAME: discourse DISCOURSE_REDIS_HOST: discourse-redis - DISCOURSE_REDIS_PORT_NUMBER: 6379 + DISCOURSE_REDIS_PORT: 6379 DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD} - # Optional: Configure SMTP for email delivery - # DISCOURSE_SMTP_HOST: ${SMTP_HOST} + DISCOURSE_DEVELOPER_EMAILS: ${DISCOURSE_ADMIN_EMAIL} + # Required for email delivery - Discourse will not function correctly without SMTP + # DISCOURSE_SMTP_ADDRESS: ${SMTP_HOST} # DISCOURSE_SMTP_PORT: ${SMTP_PORT} - # DISCOURSE_SMTP_USER: ${SMTP_USER} - # DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD} - restart: unless-stopped - - discourse-sidekiq: - image: docker.io/bitnamilegacy/discourse:3.5.0 - - volumes: - - discourse-sidekiq-data:/bitnami/discourse - depends_on: - - discourse-app - command: /opt/bitnami/scripts/discourse-sidekiq/run.sh - environment: - DISCOURSE_HOST: ${DISCOURSE_HOST} - DISCOURSE_DATABASE_HOST: discourse-db - DISCOURSE_DATABASE_PORT_NUMBER: 5432 - DISCOURSE_DATABASE_USER: bn_discourse - DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD} - DISCOURSE_DATABASE_NAME: bitnami_discourse - DISCOURSE_REDIS_HOST: discourse-redis - DISCOURSE_REDIS_PORT_NUMBER: 6379 - DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD} - # Optional: Configure SMTP for email delivery - # DISCOURSE_SMTP_HOST: ${SMTP_HOST} - # DISCOURSE_SMTP_PORT: ${SMTP_PORT} - # DISCOURSE_SMTP_USER: ${SMTP_USER} + # DISCOURSE_SMTP_USER_NAME: ${SMTP_USER} # DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD} + # DISCOURSE_SMTP_ENABLE_START_TLS: "true" + # DISCOURSE_NOTIFICATION_EMAIL: [email protected] + # DISCOURSE_DEVELOPER_EMAILS: [email protected] + ports: + - 80 restart: unless-stopped volumes: discourse-postgresql-data: discourse-redis-data: discourse-data: - discourse-sidekiq-data: \ No newline at end of file diff --git a/blueprints/discourse/template.toml b/blueprints/discourse/template.toml index c02aa2c7..10358839 100644 --- a/blueprints/discourse/template.toml +++ b/blueprints/discourse/template.toml @@ -1,11 +1,13 @@ [variables] main_domain = "${domain}" +admin_email = "${email}" postgres_password = "${password}" redis_password = "${password}" [config] env = [ "DISCOURSE_HOST=${main_domain}", + "DISCOURSE_ADMIN_EMAIL=${admin_email}", "POSTGRES_PASSWORD=${postgres_password}", "REDIS_PASSWORD=${redis_password}", ] @@ -13,5 +15,5 @@ mounts = [] [[config.domains]] serviceName = "discourse-app" -port = 3_000 -host = "${main_domain}" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/blueprints/fider/docker-compose.yml b/blueprints/fider/docker-compose.yml new file mode 100644 index 00000000..1c42adaf --- /dev/null +++ b/blueprints/fider/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.8" + +services: + db: + image: postgres:17 + restart: unless-stopped + volumes: + - fiderPostgresData:/var/lib/postgresql/data + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + + fider: + image: getfider/fider:0.21.1 + restart: unless-stopped + depends_on: + - db + - mailhog + environment: + BASE_URL: ${BASE_URL} + DATABASE_URL: ${DATABASE_URL} + JWT_SECRET: ${JWT_SECRET} + EMAIL: ${EMAIL} + EMAIL_NOREPLY: ${EMAIL_NOREPLY} + EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST} + EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT} + EMAIL_SMTP_USERNAME: ${EMAIL_SMTP_USERNAME} + EMAIL_SMTP_PASSWORD: ${EMAIL_SMTP_PASSWORD} + EMAIL_SMTP_ENABLE_STARTTLS: ${EMAIL_SMTP_ENABLE_STARTTLS} + + mailhog: + image: mailhog/mailhog:v1.0.1 + restart: unless-stopped + +volumes: + fiderPostgresData: diff --git a/blueprints/fider/fider.svg b/blueprints/fider/fider.svg new file mode 100644 index 00000000..e3b6b12c --- /dev/null +++ b/blueprints/fider/fider.svg @@ -0,0 +1,9 @@ + + Fider + Fider template logo + + + + + + \ No newline at end of file diff --git a/blueprints/fider/template.toml b/blueprints/fider/template.toml new file mode 100644 index 00000000..c409461a --- /dev/null +++ b/blueprints/fider/template.toml @@ -0,0 +1,27 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_USER=fider", + "POSTGRES_PASSWORD=${postgres_password}", + "POSTGRES_DB=fider", + "BASE_URL=https://${main_domain}", + "DATABASE_URL=postgres://fider:${postgres_password}@db:5432/fider?sslmode=disable", + "JWT_SECRET=${jwt_secret}", + "EMAIL_NOREPLY=noreply@${main_domain}", + "EMAIL=smtp", + "EMAIL_SMTP_HOST=mailhog", + "EMAIL_SMTP_PORT=1025", + "EMAIL_SMTP_USERNAME=", + "EMAIL_SMTP_PASSWORD=", + "EMAIL_SMTP_ENABLE_STARTTLS=true", +] +mounts = [] + +[[config.domains]] +serviceName = "fider" +port = 3000 +host = "${main_domain}" diff --git a/blueprints/geoserver/docker-compose.yml b/blueprints/geoserver/docker-compose.yml new file mode 100644 index 00000000..fdd4fc97 --- /dev/null +++ b/blueprints/geoserver/docker-compose.yml @@ -0,0 +1,27 @@ +services: + geoserver: + image: kartoza/geoserver:2.24.1 + depends_on: + - db + environment: + - GEOSERVER_ADMIN_USER=${GEOSERVER_ADMIN_USER} + - GEOSERVER_ADMIN_PASSWORD=${GEOSERVER_ADMIN_PASSWORD} + - INITIAL_MEMORY=${INITIAL_MEMORY} + - MAXIMUM_MEMORY=${MAXIMUM_MEMORY} + volumes: + - geoserver-data:/opt/geoserver/data_dir + restart: unless-stopped + + db: + image: postgis/postgis:17-3.4 + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + volumes: + - postgis-data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + geoserver-data: + postgis-data: diff --git a/blueprints/geoserver/logo.png b/blueprints/geoserver/logo.png new file mode 100644 index 00000000..14ae15a6 Binary files /dev/null and b/blueprints/geoserver/logo.png differ diff --git a/blueprints/geoserver/logo.svg b/blueprints/geoserver/logo.svg new file mode 100644 index 00000000..e2f02fee --- /dev/null +++ b/blueprints/geoserver/logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/blueprints/geoserver/template.toml b/blueprints/geoserver/template.toml new file mode 100644 index 00000000..a975cc8e --- /dev/null +++ b/blueprints/geoserver/template.toml @@ -0,0 +1,18 @@ +[info] +name = "GeoServer & PostGIS" +description = "An open-source server for sharing geospatial data, paired with a PostGIS database. Template created and maintained by OpenGeoCity Tanzania (info@ogctz.org). For further assistance contact +255759968919 or visit https://opengeocity.org or github: https://github.com/ShabaniMagawila or linkedin: https://www.linkedin.com/in/shabani-magawila/" +version = "2.24.1" + +[variables] +GEOSERVER_ADMIN_USER = "admin" +GEOSERVER_ADMIN_PASSWORD = "geoserver_secure" +INITIAL_MEMORY = "2G" +MAXIMUM_MEMORY = "4G" +POSTGRES_USER = "postgres" +POSTGRES_PASSWORD = "postgres_secure" +POSTGRES_DB = "gis" + +[config] +[[config.domains]] +serviceName = "geoserver" +port = 8080 \ No newline at end of file diff --git a/blueprints/glitchtip/docker-compose.yml b/blueprints/glitchtip/docker-compose.yml index f47742f0..8a483a61 100644 --- a/blueprints/glitchtip/docker-compose.yml +++ b/blueprints/glitchtip/docker-compose.yml @@ -1,55 +1,52 @@ +version: "3.8" + x-environment: &default-environment DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres + VALKEY_URL: redis://valkey:6379 SECRET_KEY: ${SECRET_KEY} PORT: ${GLITCHTIP_PORT} EMAIL_URL: consolemail:// - GLITCHTIP_DOMAIN: http://${GLITCHTIP_HOST} - DEFAULT_FROM_EMAIL: email@glitchtip.com - CELERY_WORKER_AUTOSCALE: "1,3" - CELERY_WORKER_MAX_TASKS_PER_CHILD: "10000" + GLITCHTIP_DOMAIN: ${GLITCHTIP_DOMAIN} + DEFAULT_FROM_EMAIL: ${DEFAULT_FROM_EMAIL} x-depends_on: &default-depends_on - postgres - - redis + - valkey services: postgres: - image: postgres:16 + image: postgres:18 environment: POSTGRES_HOST_AUTH_METHOD: "trust" restart: unless-stopped volumes: - pg-data:/var/lib/postgresql/data - - redis: - image: redis + valkey: + image: valkey/valkey:9 restart: unless-stopped - web: - image: glitchtip/glitchtip:v4.0 + image: glitchtip/glitchtip:6.1.0 depends_on: *default-depends_on - ports: + expose: - ${GLITCHTIP_PORT} environment: *default-environment restart: unless-stopped volumes: - uploads:/code/uploads worker: - image: glitchtip/glitchtip:v4.0 - command: ./bin/run-celery-with-beat.sh + image: glitchtip/glitchtip:6.1.0 + command: ./bin/run-worker.sh depends_on: *default-depends_on environment: *default-environment restart: unless-stopped volumes: - uploads:/code/uploads - migrate: - image: glitchtip/glitchtip:v4.0 + image: glitchtip/glitchtip:6.1.0 depends_on: *default-depends_on - command: "./manage.py migrate" + command: ./bin/run-migrate.sh environment: *default-environment - volumes: pg-data: uploads: diff --git a/blueprints/glitchtip/template.toml b/blueprints/glitchtip/template.toml index c5b7a46c..af90c5ff 100644 --- a/blueprints/glitchtip/template.toml +++ b/blueprints/glitchtip/template.toml @@ -4,7 +4,8 @@ secret_key = "${base64:32}" [config] env = [ - "GLITCHTIP_HOST=${main_domain}", + "DEFAULT_FROM_EMAIL=${email}", + "GLITCHTIP_DOMAIN=https://${main_domain}", "GLITCHTIP_PORT=8000", "SECRET_KEY=${secret_key}", ] @@ -12,5 +13,5 @@ mounts = [] [[config.domains]] serviceName = "web" -port = 8_000 +port = 8000 host = "${main_domain}" diff --git a/blueprints/hedgedoc/docker-compose.yml b/blueprints/hedgedoc/docker-compose.yml new file mode 100644 index 00000000..8ee2e567 --- /dev/null +++ b/blueprints/hedgedoc/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.8" + +services: + hedgedoc: + image: quay.io/hedgedoc/hedgedoc:1.10.8 + restart: unless-stopped + depends_on: + database: + condition: service_healthy + environment: + - CMD_DB_URL=${CMD_DB_URL} + - CMD_DOMAIN=${CMD_DOMAIN} + - CMD_PROTOCOL_USESSL=${CMD_PROTOCOL_USESSL} + - CMD_URL_ADDPORT=${CMD_URL_ADDPORT} + volumes: + - hedgedoc_uploads:/hedgedoc/public/uploads + expose: + - "3000" + + database: + image: postgres:17.7-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=hedgedoc + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=hedgedoc + volumes: + - hedgedoc_database:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U hedgedoc -d hedgedoc"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + hedgedoc_uploads: + hedgedoc_database: diff --git a/blueprints/hedgedoc/hedgedoc.svg b/blueprints/hedgedoc/hedgedoc.svg new file mode 100644 index 00000000..0d236216 --- /dev/null +++ b/blueprints/hedgedoc/hedgedoc.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/hedgedoc/template.toml b/blueprints/hedgedoc/template.toml new file mode 100644 index 00000000..8e82e9d5 --- /dev/null +++ b/blueprints/hedgedoc/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +database_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "hedgedoc" +port = 3000 +host = "${main_domain}" + +[config.env] +POSTGRES_PASSWORD = "${database_password}" +CMD_DB_URL = "postgres://hedgedoc:${database_password}@database:5432/hedgedoc" +CMD_DOMAIN = "${main_domain}" +CMD_PROTOCOL_USESSL = "true" +CMD_URL_ADDPORT = "false" diff --git a/blueprints/hi-events/docker-compose.yml b/blueprints/hi-events/docker-compose.yml index 3c2291b9..dd3d7269 100644 --- a/blueprints/hi-events/docker-compose.yml +++ b/blueprints/hi-events/docker-compose.yml @@ -1,6 +1,6 @@ services: all-in-one: - image: daveearley/hi.events-all-in-one:v0.8.0-beta.1 + image: daveearley/hi.events-all-in-one:v1.9.0-beta restart: always environment: - VITE_FRONTEND_URL=https://${DOMAIN} @@ -20,6 +20,8 @@ services: - MAIL_MAILER - MAIL_HOST - MAIL_PORT + - MAIL_USERNAME + - MAIL_PASSWORD - MAIL_FROM_ADDRESS - MAIL_FROM_NAME depends_on: diff --git a/blueprints/hi-events/template.toml b/blueprints/hi-events/template.toml index 4fea86ce..a8c2c3d3 100644 --- a/blueprints/hi-events/template.toml +++ b/blueprints/hi-events/template.toml @@ -16,6 +16,8 @@ env = [ "MAIL_MAILER=", "MAIL_HOST=", "MAIL_PORT=", + "MAIL_USERNAME=", + "MAIL_PASSWORD=", "MAIL_FROM_ADDRESS=", "MAIL_FROM_NAME=", ] diff --git a/blueprints/homepage/docker-compose.yml b/blueprints/homepage/docker-compose.yml new file mode 100644 index 00000000..2a6d48b9 --- /dev/null +++ b/blueprints/homepage/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3.8" +services: + homepage: + image: ghcr.io/gethomepage/homepage:v1.13.1 + restart: unless-stopped + expose: + - 3000 + volumes: + - ../files/homepage-config:/app/config diff --git a/blueprints/homepage/homepage.png b/blueprints/homepage/homepage.png new file mode 100644 index 00000000..0a11f3dc Binary files /dev/null and b/blueprints/homepage/homepage.png differ diff --git a/blueprints/homepage/template.toml b/blueprints/homepage/template.toml new file mode 100644 index 00000000..2f4dfa15 --- /dev/null +++ b/blueprints/homepage/template.toml @@ -0,0 +1,13 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "homepage" +port = 3000 +host = "${main_domain}" + +[config.env] +HOMEPAGE_ALLOWED_HOSTS = "${main_domain}" +PUID = "1000" +PGID = "1000" diff --git a/blueprints/imgproxy/docker-compose.yml b/blueprints/imgproxy/docker-compose.yml index e9bc86a1..b71ccf49 100644 --- a/blueprints/imgproxy/docker-compose.yml +++ b/blueprints/imgproxy/docker-compose.yml @@ -3,6 +3,8 @@ services: imgproxy: image: darthsim/imgproxy:v3.30.1 restart: unless-stopped + expose: + - 8080 environment: IMGPROXY_KEY: ${IMGPROXY_KEY} IMGPROXY_SALT: ${IMGPROXY_SALT} diff --git a/blueprints/immich/docker-compose.yml b/blueprints/immich/docker-compose.yml index 8f12d6d1..f385b329 100644 --- a/blueprints/immich/docker-compose.yml +++ b/blueprints/immich/docker-compose.yml @@ -28,7 +28,7 @@ services: REDIS_DBINDEX: ${REDIS_DBINDEX} # Server Configuration TZ: ${TZ} - restart: always + restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:2283/server-info/ping"] interval: 30s @@ -44,7 +44,7 @@ services: REDIS_HOSTNAME: ${REDIS_HOSTNAME} REDIS_PORT: ${REDIS_PORT} REDIS_DBINDEX: ${REDIS_DBINDEX} - restart: always + restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3003/ping"] interval: 30s @@ -61,7 +61,7 @@ services: interval: 10s timeout: 5s retries: 5 - restart: always + restart: unless-stopped immich-database: image: tensorchord/pgvecto-rs:pg14-v0.3.0 @@ -94,10 +94,10 @@ services: '-c', 'wal_compression=on', ] - restart: always + restart: unless-stopped volumes: immich-model-cache: immich-postgres: immich-library: - immich-redis-data: + immich-redis-data: diff --git a/blueprints/joomla/docker-compose.yml b/blueprints/joomla/docker-compose.yml new file mode 100644 index 00000000..e83a420b --- /dev/null +++ b/blueprints/joomla/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + joomla: + image: joomla:6.1.0-apache + restart: unless-stopped + depends_on: + joomla-db: + condition: service_healthy + expose: + - "80" + environment: + JOOMLA_DB_HOST: joomla-db + JOOMLA_DB_USER: ${JOOMLA_DB_USER} + JOOMLA_DB_PASSWORD: ${JOOMLA_DB_PASSWORD} + JOOMLA_DB_NAME: ${JOOMLA_DB_NAME} + JOOMLA_SITE_NAME: ${JOOMLA_SITE_NAME} + JOOMLA_ADMIN_USER: ${JOOMLA_ADMIN_USER} + JOOMLA_ADMIN_USERNAME: ${JOOMLA_ADMIN_USERNAME} + JOOMLA_ADMIN_PASSWORD: ${JOOMLA_ADMIN_PASSWORD} + JOOMLA_ADMIN_EMAIL: ${JOOMLA_ADMIN_EMAIL} + volumes: + - joomla-data:/var/www/html + + joomla-db: + image: mariadb:11.4 + restart: unless-stopped + environment: + MARIADB_DATABASE: ${JOOMLA_DB_NAME} + MARIADB_USER: ${JOOMLA_DB_USER} + MARIADB_PASSWORD: ${JOOMLA_DB_PASSWORD} + MARIADB_RANDOM_ROOT_PASSWORD: "1" + volumes: + - joomla-db:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + joomla-data: + joomla-db: diff --git a/blueprints/joomla/joomla.svg b/blueprints/joomla/joomla.svg new file mode 100644 index 00000000..27239ba8 --- /dev/null +++ b/blueprints/joomla/joomla.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/blueprints/joomla/template.toml b/blueprints/joomla/template.toml new file mode 100644 index 00000000..4f360d66 --- /dev/null +++ b/blueprints/joomla/template.toml @@ -0,0 +1,51 @@ +[variables] +main_domain = "${domain}" +site_name = "Joomla" +admin_name = "Joomla Administrator" +admin_username = "${username}" +admin_password = "${password:24}" +admin_email = "${email}" +db_user = "joomla" +db_password = "${password:32}" +db_name = "joomla" + +[[config.domains]] +serviceName = "joomla" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Joomla + +This template deploys Joomla CMS with MariaDB using the official Joomla Apache image. + +## Access + +- URL: `http://${main_domain}` +- Administrator username: the configured `admin_username` value +- Administrator password: the generated `admin_password` value +- Administrator email: the configured `admin_email` value + +The template uses Joomla's auto-deployment environment variables so the site and administrator account are initialized on first start. + +## Storage + +The template creates persistent volumes for: + +- Joomla application files at `/var/www/html` +- MariaDB data at `/var/lib/mysql` + +Back up both volumes together before upgrades or migrations. +""" + +[config.env] +JOOMLA_SITE_NAME = "${site_name}" +JOOMLA_ADMIN_USER = "${admin_name}" +JOOMLA_ADMIN_USERNAME = "${admin_username}" +JOOMLA_ADMIN_PASSWORD = "${admin_password}" +JOOMLA_ADMIN_EMAIL = "${admin_email}" +JOOMLA_DB_USER = "${db_user}" +JOOMLA_DB_PASSWORD = "${db_password}" +JOOMLA_DB_NAME = "${db_name}" diff --git a/blueprints/langflow/docker-compose.yml b/blueprints/langflow/docker-compose.yml index a9628286..70495a7a 100644 --- a/blueprints/langflow/docker-compose.yml +++ b/blueprints/langflow/docker-compose.yml @@ -1,31 +1,44 @@ -version: "3.8" - services: langflow: - image: langflowai/langflow:v1.1.1 + image: langflowai/langflow:latest + user: root + restart: always + pull_policy: always ports: - 7860 depends_on: - postgres-langflow environment: - LANGFLOW_DATABASE_URL=postgresql://${DB_USERNAME}:${DB_PASSWORD}@postgres-langflow:5432/langflow - # This variable defines where the logs, file storage, monitor data and secret keys are stored. + - LANGFLOW_CONFIG_DIR=/app/config + - LANGFLOW_SUPERUSER=email@domain.com + - LANGFLOW_SUPERUSER_PASSWORD=changepassword + - LANGFLOW_SECRET_KEY=PP_G4Gwm1lOkyG8r8N0LrdlpWXZ7Tyq5CVyfBquuj6g= + - DO_NOT_TRACK=True + - LANGFLOW_AUTO_SAVING=True + - LANGFLOW_AUTO_LOGIN=False + - LANGFLOW_NEW_USER_IS_ACTIVE=False + - LANGFLOW_ENABLE_SUPERUSER_CLI=False volumes: - - langflow-data:/app/langflow - - + - app-data:/app/langflow + - app-config:/app/config + postgres-langflow: image: postgres:16 + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME} -d langflow"] + interval: 3s + timeout: 3s + retries: 10 environment: POSTGRES_USER: ${DB_USERNAME} POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: langflow - ports: - - 5432 volumes: - - langflow-postgres:/var/lib/postgresql/data - + - postgres-data:/var/lib/postgresql/data volumes: - langflow-postgres: - langflow-data: \ No newline at end of file + postgres-data: + app-data: + app-config: diff --git a/blueprints/libredb-studio/docker-compose.yml b/blueprints/libredb-studio/docker-compose.yml new file mode 100644 index 00000000..00b88766 --- /dev/null +++ b/blueprints/libredb-studio/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" +services: + libredb-studio: + image: ghcr.io/libredb/libredb-studio:0.9.27 + restart: unless-stopped + environment: + - ADMIN_EMAIL=admin@libredb.org + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - USER_EMAIL=user@libredb.org + - USER_PASSWORD=${USER_PASSWORD} + - JWT_SECRET=${JWT_SECRET} + - STORAGE_PROVIDER=sqlite + - STORAGE_SQLITE_PATH=/app/data/libredb-storage.db + volumes: + - libredb-data:/app/data +volumes: + libredb-data: diff --git a/blueprints/libredb-studio/libredb-studio.svg b/blueprints/libredb-studio/libredb-studio.svg new file mode 100644 index 00000000..c36a98eb --- /dev/null +++ b/blueprints/libredb-studio/libredb-studio.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blueprints/libredb-studio/template.toml b/blueprints/libredb-studio/template.toml new file mode 100644 index 00000000..9d30084d --- /dev/null +++ b/blueprints/libredb-studio/template.toml @@ -0,0 +1,18 @@ +[variables] +admin_password = "${password:32}" +user_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +[[config.domains]] +serviceName = "libredb-studio" +port = 3000 + +[[config.env]] +ADMIN_PASSWORD = "${admin_password}" + +[[config.env]] +USER_PASSWORD = "${user_password}" + +[[config.env]] +JWT_SECRET = "${jwt_secret}" diff --git a/blueprints/librespeed/docker-compose.yml b/blueprints/librespeed/docker-compose.yml new file mode 100644 index 00000000..2f7e8bc1 --- /dev/null +++ b/blueprints/librespeed/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + librespeed: + image: lscr.io/linuxserver/librespeed:latest + restart: unless-stopped + expose: + - 80 + volumes: + - librespeed-config:/config + environment: + - PUID=1000 + - PGID=1000 + - TZ=${TZ} + - PASSWORD=${PASSWORD} + - CUSTOM_RESULTS=${CUSTOM_RESULTS} + - DB_TYPE=sqlite + +volumes: + librespeed-config: diff --git a/blueprints/librespeed/librespeed.svg b/blueprints/librespeed/librespeed.svg new file mode 100644 index 00000000..c76c99ec --- /dev/null +++ b/blueprints/librespeed/librespeed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/blueprints/librespeed/template.toml b/blueprints/librespeed/template.toml new file mode 100644 index 00000000..b8ed3e13 --- /dev/null +++ b/blueprints/librespeed/template.toml @@ -0,0 +1,14 @@ +[variables] +main_domain = "${domain}" +password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "librespeed" +port = 80 +host = "${main_domain}" + +[config.env] +PASSWORD = "${password}" +TZ = "UTC" +CUSTOM_RESULTS = "false" diff --git a/blueprints/limesurvey/docker-compose.yml b/blueprints/limesurvey/docker-compose.yml new file mode 100644 index 00000000..936e5199 --- /dev/null +++ b/blueprints/limesurvey/docker-compose.yml @@ -0,0 +1,40 @@ +version: "3.8" + +services: + db: + image: mariadb:10.11 + restart: always + volumes: + - db_data:/var/lib/mysql + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=limesurvey + - MYSQL_USER=limesurvey + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + + limesurvey: + image: martialblog/limesurvey:latest + restart: always + volumes: + - limesurvey_upload:/var/www/html/upload + environment: + - ADMIN_EMAIL=${ADMIN_EMAIL} + - ADMIN_NAME=Admin + - ADMIN_USER=admin + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + - DB_HOST=db + - DB_MYSQL_ENGINE=InnoDB + - DB_NAME=limesurvey + - DB_PASSWORD=${MYSQL_PASSWORD} + - DB_USERNAME=limesurvey + - DEBUG=0 + - DEBUG_SQL=0 + - PUBLIC_URL=${PUBLIC_URL} + - SHOW_SCRIPT_NAME=false + - URL_FORMAT=path + depends_on: + - db + +volumes: + db_data: + limesurvey_upload: diff --git a/blueprints/limesurvey/limesurvey.svg b/blueprints/limesurvey/limesurvey.svg new file mode 100644 index 00000000..9ee4c0c6 --- /dev/null +++ b/blueprints/limesurvey/limesurvey.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + diff --git a/blueprints/limesurvey/template.toml b/blueprints/limesurvey/template.toml new file mode 100644 index 00000000..07623f50 --- /dev/null +++ b/blueprints/limesurvey/template.toml @@ -0,0 +1,19 @@ +[variables] +main_domain = "${domain}" +mysql_root_password = "${password:32}" +mysql_user_password = "${password:32}" +admin_email = "${email}" +admin_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "limesurvey" +port = 8080 +host = "${main_domain}" + +[config.env] +PUBLIC_URL = "http://${main_domain}" +ADMIN_EMAIL = "${admin_email}" +ADMIN_PASSWORD = "${admin_password}" +MYSQL_PASSWORD = "${mysql_user_password}" +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" diff --git a/blueprints/lubelogger/docker-compose.yml b/blueprints/lubelogger/docker-compose.yml new file mode 100644 index 00000000..535b8e55 --- /dev/null +++ b/blueprints/lubelogger/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + lubelogger: + image: ghcr.io/hargata/lubelogger:v1.6.5 + restart: unless-stopped + environment: + - LC_ALL=${LUBELOGGER_LOCALE} + - LANG=${LUBELOGGER_LOCALE} + - LUBELOGGER_DOMAIN=${LUBELOGGER_DOMAIN} + - EnableAuth=${LUBELOGGER_ENABLE_AUTH} + volumes: + - lubelogger-data:/App/data + - lubelogger-keys:/root/.aspnet/DataProtection-Keys + expose: + - 8080 + +volumes: + lubelogger-data: {} + lubelogger-keys: {} diff --git a/blueprints/lubelogger/lubelogger.png b/blueprints/lubelogger/lubelogger.png new file mode 100644 index 00000000..b3713e80 Binary files /dev/null and b/blueprints/lubelogger/lubelogger.png differ diff --git a/blueprints/lubelogger/template.toml b/blueprints/lubelogger/template.toml new file mode 100644 index 00000000..4ff28963 --- /dev/null +++ b/blueprints/lubelogger/template.toml @@ -0,0 +1,18 @@ +[variables] +main_domain = "${domain}" +lubelogger_locale = "en_US.UTF-8" +lubelogger_enable_auth = "false" +lubelogger_domain = "https://${main_domain}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "lubelogger" +port = 8080 +host = "${main_domain}" + +[config.env] +LUBELOGGER_LOCALE = "${lubelogger_locale}" +LUBELOGGER_ENABLE_AUTH = "${lubelogger_enable_auth}" +LUBELOGGER_DOMAIN = "${lubelogger_domain}" diff --git a/blueprints/marketing-dashboard/docker-compose.yml b/blueprints/marketing-dashboard/docker-compose.yml new file mode 100644 index 00000000..637849e7 --- /dev/null +++ b/blueprints/marketing-dashboard/docker-compose.yml @@ -0,0 +1,47 @@ +services: + marketing-dashboard: + image: ghcr.io/abel-yelin/marketing-dashboard:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + DATABASE_URL: ${DATABASE_URL} + DIRECT_URL: ${DIRECT_URL} + MARKETING_APP_URL: ${MARKETING_APP_URL} + MARKETING_COLLECTOR_UPSTREAM_URL: ${MARKETING_COLLECTOR_UPSTREAM_URL} + MARKETING_SESSION_TIMEOUT_MINUTES: ${MARKETING_SESSION_TIMEOUT_MINUTES} + MARKETING_PROXY_TIMEOUT_MS: ${MARKETING_PROXY_TIMEOUT_MS} + MARKETING_INGEST_MAX_BODY_BYTES: ${MARKETING_INGEST_MAX_BODY_BYTES} + MARKETING_INGEST_IP_RATE_LIMIT: ${MARKETING_INGEST_IP_RATE_LIMIT} + MARKETING_INGEST_CLIENT_RATE_LIMIT: ${MARKETING_INGEST_CLIENT_RATE_LIMIT} + MARKETING_INGEST_RATE_LIMIT_WINDOW_MS: ${MARKETING_INGEST_RATE_LIMIT_WINDOW_MS} + MARKETING_ENABLE_PILOT_API: ${MARKETING_ENABLE_PILOT_API} + MARKETING_DASHBOARD_ADMIN_USER: ${MARKETING_DASHBOARD_ADMIN_USER} + MARKETING_DASHBOARD_ADMIN_PASSWORD: ${MARKETING_DASHBOARD_ADMIN_PASSWORD} + ports: + - 3000 + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/health > /dev/null || exit 1"] + interval: 15s + timeout: 5s + retries: 10 + start_period: 30s + + postgres: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - marketing-dashboard-postgres:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""] + interval: 10s + timeout: 5s + retries: 10 + +volumes: + marketing-dashboard-postgres: diff --git a/blueprints/marketing-dashboard/logo.svg b/blueprints/marketing-dashboard/logo.svg new file mode 100644 index 00000000..07a4ae88 --- /dev/null +++ b/blueprints/marketing-dashboard/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/marketing-dashboard/template.toml b/blueprints/marketing-dashboard/template.toml new file mode 100644 index 00000000..531874ea --- /dev/null +++ b/blueprints/marketing-dashboard/template.toml @@ -0,0 +1,30 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +admin_password = "${password:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "marketing-dashboard" +port = 3000 +host = "${main_domain}" + +[config.env] +POSTGRES_DB = "marketing_dashboard" +POSTGRES_USER = "marketing_dashboard" +POSTGRES_PASSWORD = "${postgres_password}" +DATABASE_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public" +DIRECT_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public" +MARKETING_APP_URL = "https://${main_domain}" +MARKETING_COLLECTOR_UPSTREAM_URL = "https://${main_domain}/api/ingest/browser" +MARKETING_SESSION_TIMEOUT_MINUTES = "30" +MARKETING_PROXY_TIMEOUT_MS = "4000" +MARKETING_INGEST_MAX_BODY_BYTES = "65536" +MARKETING_INGEST_IP_RATE_LIMIT = "300" +MARKETING_INGEST_CLIENT_RATE_LIMIT = "1200" +MARKETING_INGEST_RATE_LIMIT_WINDOW_MS = "60000" +MARKETING_ENABLE_PILOT_API = "false" +MARKETING_DASHBOARD_ADMIN_USER = "admin" +MARKETING_DASHBOARD_ADMIN_PASSWORD = "${admin_password}" diff --git a/blueprints/maxio/docker-compose.yml b/blueprints/maxio/docker-compose.yml new file mode 100644 index 00000000..51a826b2 --- /dev/null +++ b/blueprints/maxio/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.8" + +services: + maxio: + image: ghcr.io/coollabsio/maxio:0.2.1 + expose: + - 9000 + volumes: + - maxio-data:/data + environment: + - MAXIO_ACCESS_KEY=${MAXIO_ACCESS_KEY} + - MAXIO_SECRET_KEY=${MAXIO_SECRET_KEY} + restart: unless-stopped +volumes: + maxio-data: diff --git a/blueprints/maxio/maxio.svg b/blueprints/maxio/maxio.svg new file mode 100644 index 00000000..0121fc4a --- /dev/null +++ b/blueprints/maxio/maxio.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/blueprints/maxio/template.toml b/blueprints/maxio/template.toml new file mode 100644 index 00000000..42e1087d --- /dev/null +++ b/blueprints/maxio/template.toml @@ -0,0 +1,10 @@ +[[config.domains]] +serviceName = "maxio" +port = 9000 +host = "${domain}" + +[config] +env = [ + "MAXIO_ACCESS_KEY=${password:20}", + "MAXIO_SECRET_KEY=${password:20}", +] diff --git a/blueprints/mediafetch/docker-compose.yml b/blueprints/mediafetch/docker-compose.yml index f0c690cd..17ba6176 100644 --- a/blueprints/mediafetch/docker-compose.yml +++ b/blueprints/mediafetch/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: mediafetch: - image: lukedunsmoto/mediafetch:latest + image: lukedunsmoto/mediafetch:1.3.5 restart: unless-stopped expose: - "3002" @@ -11,8 +11,11 @@ services: - PORT=3002 - BASIC_AUTH_USER=${BASIC_AUTH_USER} - BASIC_AUTH_PASS=${BASIC_AUTH_PASS} - - PUBLIC_BASE_URL=https://${DOMAIN} - - OUTPUT_DIR=/data/downloads + - PUBLIC_BASE_URL=${PUBLIC_BASE_URL} + - OUTPUT_DIR=${OUTPUT_DIR} + - MAX_CONCURRENT_JOBS=${MAX_CONCURRENT_JOBS} + - JOB_TIMEOUT_MS=${JOB_TIMEOUT_MS} + - VERSION_CHECK_TTL_MS=${VERSION_CHECK_TTL_MS} volumes: mediafetch_data: \ No newline at end of file diff --git a/blueprints/mediafetch/template.toml b/blueprints/mediafetch/template.toml index 07672d4e..791bd07f 100644 --- a/blueprints/mediafetch/template.toml +++ b/blueprints/mediafetch/template.toml @@ -1,13 +1,21 @@ [variables] +main_domain = "${domain}" +PORT="3002" +PUBLIC_BASE_URL="https://${main_domain}" +OUTPUT_DIR="/data/downloads" BASIC_AUTH_USER = "admin" -BASIC_AUTH_PASS = "${password:12}" +BASIC_AUTH_PASS = "changeme" +MAX_CONCURRENT_JOBS="2" +JOB_TIMEOUT_MS="600000" +VERSION_CHECK_TTL_MS="21600000" [config] + [[config.domains]] name = "Domain" -variable = "DOMAIN" serviceName = "mediafetch" -port = 3002 +port = "3002" +host = "${main_domain}" [[config.mounts]] name = "Downloads" diff --git a/blueprints/miniflux/docker-compose.yml b/blueprints/miniflux/docker-compose.yml new file mode 100644 index 00000000..874021ac --- /dev/null +++ b/blueprints/miniflux/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.8" + +services: + miniflux: + image: miniflux/miniflux:2.2.19 + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + DATABASE_URL: user=miniflux password=${POSTGRES_PASSWORD} dbname=miniflux host=db sslmode=disable + RUN_MIGRATIONS: "1" + CREATE_ADMIN: "1" + ADMIN_USERNAME: ${ADMIN_USERNAME} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + BASE_URL: https://${MINIFLUX_DOMAIN} + expose: + - "8080" + healthcheck: + test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + + db: + image: postgres:18-alpine + restart: unless-stopped + environment: + POSTGRES_USER: miniflux + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: miniflux + expose: + - "5432" + volumes: + - miniflux-db:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U miniflux -d miniflux"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + miniflux-db: diff --git a/blueprints/miniflux/miniflux.svg b/blueprints/miniflux/miniflux.svg new file mode 100644 index 00000000..3537ce7e --- /dev/null +++ b/blueprints/miniflux/miniflux.svg @@ -0,0 +1 @@ + diff --git a/blueprints/miniflux/template.toml b/blueprints/miniflux/template.toml new file mode 100644 index 00000000..d6f553f7 --- /dev/null +++ b/blueprints/miniflux/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +admin_username = "admin" +admin_password = "${password:32}" + +[config] +[[config.domains]] +serviceName = "miniflux" +port = 8080 +host = "${main_domain}" + +[config.env] +MINIFLUX_DOMAIN = "${main_domain}" +POSTGRES_PASSWORD = "${postgres_password}" +ADMIN_USERNAME = "${admin_username}" +ADMIN_PASSWORD = "${admin_password}" diff --git a/blueprints/nextcloud-aio/docker-compose.yml b/blueprints/nextcloud-aio/docker-compose.yml index 9cec8542..7550c252 100644 --- a/blueprints/nextcloud-aio/docker-compose.yml +++ b/blueprints/nextcloud-aio/docker-compose.yml @@ -14,6 +14,21 @@ services: - nextcloud_db - nextcloud_redis + nextcloud_cron: + image: nextcloud:stable + restart: always + volumes: + - nextcloud_data:/var/www/html + environment: + - MYSQL_HOST=nextcloud_db + - MYSQL_DATABASE=nextcloud + - MYSQL_USER=nextcloud + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + entrypoint: /cron.sh + depends_on: + - nextcloud_db + - nextcloud_redis + nextcloud_db: image: mariadb:10.11 restart: always @@ -31,4 +46,4 @@ services: volumes: nextcloud_data: - nextcloud_db_data: \ No newline at end of file + nextcloud_db_data: diff --git a/blueprints/omniroute/docker-compose.yml b/blueprints/omniroute/docker-compose.yml new file mode 100644 index 00000000..552c8f26 --- /dev/null +++ b/blueprints/omniroute/docker-compose.yml @@ -0,0 +1,40 @@ +services: + omniroute: + image: diegosouzapw/omniroute:3.8.17 + restart: unless-stopped + + expose: + - "20128" + - "20129" + + volumes: + - omniroute-prod-data:/app/data + + environment: + - NODE_ENV=production + - PORT=${PORT:-20128} + - DASHBOARD_PORT=${DASHBOARD_PORT:-${PORT:-20128}} + - API_PORT=${API_PORT:-20129} + - API_HOST=${API_HOST:-0.0.0.0} + - HOSTNAME=0.0.0.0 + - DATA_DIR=/app/data + - PATH=/home/node/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + + redis: + image: redis:8.6.2-alpine + restart: unless-stopped + volumes: + - redis-prod-data:/data + command: redis-server --save 60 1 --loglevel warning + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 10s + timeout: 5s + retries: 3 + +volumes: + omniroute-prod-data: + + redis-prod-data: + + diff --git a/blueprints/omniroute/omniroute.png b/blueprints/omniroute/omniroute.png new file mode 100644 index 00000000..4d4a0d14 Binary files /dev/null and b/blueprints/omniroute/omniroute.png differ diff --git a/blueprints/omniroute/template.toml b/blueprints/omniroute/template.toml new file mode 100644 index 00000000..ae4a61e0 --- /dev/null +++ b/blueprints/omniroute/template.toml @@ -0,0 +1,31 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "omniroute" +port = 20128 +host = "${main_domain}" + +[config.env] +JWT_SECRET = "${base64:64}" +API_KEY_SECRET = "${base64:64}" +INITIAL_PASSWORD = "${base64:64}" +STORAGE_ENCRYPTION_KEY = "${base64:64}" +STORAGE_ENCRYPTION_KEY_VERSION = "v1" +DISABLE_SQLITE_AUTO_BACKUP = false +PORT = 20128 +API_PORT = 20129 +OMNIROUTE_USE_TURBOPACK = 1 +NODE_ENV = "production" +MACHINE_ID_SALT = "endpoint-proxy-salt" +AUTH_COOKIE_SECURE = false +REQUIRE_API_KEY = false +ALLOW_API_KEY_REVEAL = false +BASE_URL = "http://localhost:20128" +CLOUD_URL = "" +NEXT_PUBLIC_BASE_URL = "http://localhost:20128" +ENABLE_SOCKS5_PROXY = true +NEXT_PUBLIC_ENABLE_SOCKS5_PROXY = true +PROVIDER_LIMITS_SYNC_INTERVAL_MINUTES = 70 +APP_LOG_TO_FILE = true diff --git a/blueprints/openobserve/docker-compose.yml b/blueprints/openobserve/docker-compose.yml new file mode 100644 index 00000000..442ec844 --- /dev/null +++ b/blueprints/openobserve/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3.8" + +services: + openobserve: + image: public.ecr.aws/zinclabs/openobserve:v0.70.0 + restart: unless-stopped + environment: + ZO_ROOT_USER_EMAIL: ${ZO_ROOT_USER_EMAIL} + ZO_ROOT_USER_PASSWORD: ${ZO_ROOT_USER_PASSWORD} + ZO_DATA_DIR: ${ZO_DATA_DIR} + expose: + - 5080 + - 5081 + volumes: + - data:${ZO_DATA_DIR} + +volumes: + data: diff --git a/blueprints/openobserve/openobserve.svg b/blueprints/openobserve/openobserve.svg new file mode 100644 index 00000000..e189fa50 --- /dev/null +++ b/blueprints/openobserve/openobserve.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/blueprints/openobserve/template.toml b/blueprints/openobserve/template.toml new file mode 100644 index 00000000..58a7b09a --- /dev/null +++ b/blueprints/openobserve/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +root_email = "${email}" +root_password = "${password:32}" + +[config] +env = [ + "ZO_ROOT_USER_EMAIL=${root_email}", + "ZO_ROOT_USER_PASSWORD=${root_password}", + "ZO_DATA_DIR=/data", +] +mounts = [] + +[[config.domains]] +serviceName = "openobserve" +port = 5080 +host = "${main_domain}" diff --git a/blueprints/opensourcepos/docker-compose.yml b/blueprints/opensourcepos/docker-compose.yml new file mode 100644 index 00000000..95d58960 --- /dev/null +++ b/blueprints/opensourcepos/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3.8" + +services: + opensourcepos: + image: jekkos/opensourcepos:master + restart: unless-stopped + depends_on: + opensourcepos-db: + condition: service_healthy + expose: + - "80" + environment: + CI_ENVIRONMENT: production + ALLOWED_HOSTNAMES: ${ALLOWED_HOSTNAMES} + FORCE_HTTPS: ${FORCE_HTTPS} + PHP_TIMEZONE: ${PHP_TIMEZONE} + MYSQL_USERNAME: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + MYSQL_DB_NAME: ${MYSQL_DATABASE} + MYSQL_HOST_NAME: opensourcepos-db + volumes: + - opensourcepos-uploads:/app/public/uploads + - opensourcepos-logs:/app/writable/logs + + opensourcepos-db: + image: mariadb:10.11 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ${MYSQL_DATABASE} + MYSQL_USER: ${MYSQL_USER} + MYSQL_PASSWORD: ${MYSQL_PASSWORD} + volumes: + - opensourcepos-db:/var/lib/mysql + healthcheck: + test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + +volumes: + opensourcepos-db: + opensourcepos-uploads: + opensourcepos-logs: diff --git a/blueprints/opensourcepos/opensourcepos.svg b/blueprints/opensourcepos/opensourcepos.svg new file mode 100644 index 00000000..d63b2009 --- /dev/null +++ b/blueprints/opensourcepos/opensourcepos.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/blueprints/opensourcepos/template.toml b/blueprints/opensourcepos/template.toml new file mode 100644 index 00000000..d925ad0c --- /dev/null +++ b/blueprints/opensourcepos/template.toml @@ -0,0 +1,51 @@ +[variables] +main_domain = "${domain}" +mysql_root_password = "${password:32}" +mysql_password = "${password:32}" +php_timezone = "UTC" +force_https = "false" +allowed_hostnames = "${main_domain}" + +[[config.domains]] +serviceName = "opensourcepos" +port = 80 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# OpenSourcePOS + +This template deploys Open Source Point of Sale with MariaDB using the upstream Docker image and persistent storage for uploads, logs, and database data. + +## Access + +- URL: `http://${main_domain}` +- Default application user: `admin` +- Default application password: `pointofsale` + +Change the default application password immediately after the first login. + +## Hostnames and HTTPS + +OpenSourcePOS validates incoming host headers in production. The `allowed_hostnames` variable defaults to `${main_domain}` and can be set to a comma-separated list such as `pos.example.com,www.pos.example.com`. + +The template defaults `force_https` to `false` because Dokploy terminates TLS outside the container. Set it to `true` only when your deployment always reaches OpenSourcePOS through HTTPS and the reverse proxy sends the expected headers. + +## Storage + +The template creates persistent volumes for: + +- MariaDB data +- uploaded files +- application logs +""" + +[config.env] +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" +MYSQL_DATABASE = "ospos" +MYSQL_USER = "admin" +MYSQL_PASSWORD = "${mysql_password}" +PHP_TIMEZONE = "${php_timezone}" +FORCE_HTTPS = "${force_https}" +ALLOWED_HOSTNAMES = "${allowed_hostnames}" diff --git a/blueprints/outline/docker-compose.yml b/blueprints/outline/docker-compose.yml index aaf98ac0..9f726040 100644 --- a/blueprints/outline/docker-compose.yml +++ b/blueprints/outline/docker-compose.yml @@ -12,18 +12,36 @@ services: NODE_ENV: production URL: ${URL} FORCE_HTTPS: 'false' + PROXY_HEADERS_TRUSTED: 'true' SECRET_KEY: ${SECRET_KEY} UTILS_SECRET: ${UTILS_SECRET} DATABASE_URL: postgres://outline:${POSTGRES_PASSWORD}@postgres:5432/outline PGSSLMODE: disable REDIS_URL: redis://redis:6379 + FILE_STORAGE: local + FILE_STORAGE_LOCAL_ROOT_DIR: /var/lib/outline/data OIDC_CLIENT_ID: outline OIDC_CLIENT_SECRET: ${CLIENT_SECRET} OIDC_AUTH_URI: ${DEX_URL}/auth OIDC_TOKEN_URI: ${DEX_URL}/token OIDC_USERINFO_URI: ${DEX_URL}/userinfo + OIDC_USERNAME_CLAIM: preferred_username + OIDC_DISPLAY_NAME: OpenID Connect + OIDC_SCOPES: openid profile email + volumes: + - outline_data-test-outline-khufpx:/var/lib/outline/data dex: + image: nginx:1.27-alpine + restart: always + depends_on: + - dex-server + volumes: + - ../files/etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf + ports: + - 5556 + + dex-server: image: ghcr.io/dexidp/dex:v2.37.0 restart: always volumes: @@ -32,8 +50,6 @@ services: - dex - serve - /etc/dex/config.yaml - ports: - - 5556 postgres: image: postgres:15 @@ -53,5 +69,6 @@ services: - redis_data-test-outline-khufpx:/data volumes: + outline_data-test-outline-khufpx: postgres_data-test-outline-khufpx: - redis_data-test-outline-khufpx: \ No newline at end of file + redis_data-test-outline-khufpx: diff --git a/blueprints/outline/template.toml b/blueprints/outline/template.toml index e4f86738..a38a16e0 100644 --- a/blueprints/outline/template.toml +++ b/blueprints/outline/template.toml @@ -1,6 +1,6 @@ [variables] main_domain = "${domain}" -dex_domain = "${domain}" +dex_domain = "dex-${domain}" secret_key = "${hash:64}" utils_secret = "${base64:32}" client_secret = "${base64:32}" @@ -17,8 +17,8 @@ port = 5_556 host = "${dex_domain}" [config.env] -URL = "http://${main_domain}" -DEX_URL = "http://${dex_domain}" +URL = "https://${main_domain}" +DEX_URL = "https://${dex_domain}" DOMAIN_NAME = "${main_domain}" POSTGRES_PASSWORD = "${postgres_password}" SECRET_KEY = "${secret_key}" @@ -28,7 +28,7 @@ CLIENT_SECRET = "${client_secret}" [[config.mounts]] filePath = "/etc/dex/config.yaml" content = """ -issuer: http://${dex_domain} +issuer: https://${dex_domain} web: http: 0.0.0.0:5556 @@ -60,7 +60,28 @@ oauth2: staticClients: - id: "outline" redirectURIs: - - http://${main_domain}/auth/oidc.callback + - https://${main_domain}/auth/oidc.callback name: "Outline" secret: "${client_secret}" """ + +[[config.mounts]] +filePath = "/etc/nginx/conf.d/default.conf" +content = """ +server { + listen 5556; + + location = / { + add_header Content-Type text/plain; + return 200 "Dex ready\n"; + } + + location / { + proxy_pass http://dex-server:5556; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +""" diff --git a/blueprints/paperclip/README.md b/blueprints/paperclip/README.md new file mode 100644 index 00000000..2ccae853 --- /dev/null +++ b/blueprints/paperclip/README.md @@ -0,0 +1,15 @@ +# Paperclip + +Paperclip is an open-source control plane for managing teams of AI agents, goals, budgets, tickets, and recurring work. + +This Dokploy template includes: + +- Paperclip web/API service using the official GHCR image +- PostgreSQL 17 database with health check +- Generated `BETTER_AUTH_SECRET` +- Generated database password +- Persistent `/paperclip` storage for instance data +- Public URL configured from the Dokploy domain +- Dokploy domain routing to Paperclip on port 3100 + +After deployment, check the Paperclip container logs for first-run setup or invite information. \ No newline at end of file diff --git a/blueprints/paperclip/docker-compose.yml b/blueprints/paperclip/docker-compose.yml new file mode 100644 index 00000000..5c1ce7bf --- /dev/null +++ b/blueprints/paperclip/docker-compose.yml @@ -0,0 +1,44 @@ +version: "3.8" + +services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: paperclip + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: paperclip + healthcheck: + test: ["CMD-SHELL", "pg_isready -U paperclip -d paperclip"] + interval: 10s + timeout: 5s + retries: 10 + volumes: + - postgres-data:/var/lib/postgresql/data + + paperclip: + image: ghcr.io/paperclipai/paperclip:latest + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + HOST: 0.0.0.0 + PORT: "3100" + SERVE_UI: "true" + DATABASE_URL: ${DATABASE_URL} + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} + PAPERCLIP_HOME: /paperclip + PAPERCLIP_INSTANCE_ID: default + PAPERCLIP_CONFIG: /paperclip/instances/default/config.json + PAPERCLIP_DEPLOYMENT_MODE: ${PAPERCLIP_DEPLOYMENT_MODE} + PAPERCLIP_DEPLOYMENT_EXPOSURE: ${PAPERCLIP_DEPLOYMENT_EXPOSURE} + PAPERCLIP_PUBLIC_URL: ${PAPERCLIP_PUBLIC_URL} + expose: + - "3100" + volumes: + - paperclip-data:/paperclip + +volumes: + postgres-data: {} + paperclip-data: {} \ No newline at end of file diff --git a/blueprints/paperclip/logo.svg b/blueprints/paperclip/logo.svg new file mode 100644 index 00000000..44a8f649 --- /dev/null +++ b/blueprints/paperclip/logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/blueprints/paperclip/template.toml b/blueprints/paperclip/template.toml new file mode 100644 index 00000000..6a79e07c --- /dev/null +++ b/blueprints/paperclip/template.toml @@ -0,0 +1,20 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +better_auth_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "DATABASE_URL=postgres://paperclip:${postgres_password}@postgres:5432/paperclip", + "BETTER_AUTH_SECRET=${better_auth_secret}", + "PAPERCLIP_DEPLOYMENT_MODE=authenticated", + "PAPERCLIP_DEPLOYMENT_EXPOSURE=private", + "PAPERCLIP_PUBLIC_URL=https://${main_domain}" +] +mounts = [] + +[[config.domains]] +serviceName = "paperclip" +port = 3100 +host = "${main_domain}" \ No newline at end of file diff --git a/blueprints/plane/docker-compose.yml b/blueprints/plane/docker-compose.yml index 9502c010..a79f83d6 100644 --- a/blueprints/plane/docker-compose.yml +++ b/blueprints/plane/docker-compose.yml @@ -2,8 +2,8 @@ version: "3.8" services: web: - image: makeplane/plane-frontend:${APP_RELEASE:-v0.27.1} - command: node web/server.js web + image: makeplane/plane-frontend:${APP_RELEASE:-v1.2.3} + restart: always depends_on: - api - worker @@ -11,8 +11,8 @@ services: - .env space: - image: makeplane/plane-space:${APP_RELEASE:-v0.27.1} - command: node space/server.js space + image: makeplane/plane-space:${APP_RELEASE:-v1.2.3} + restart: always depends_on: - api - worker @@ -21,8 +21,8 @@ services: - .env admin: - image: makeplane/plane-admin:${APP_RELEASE:-v0.27.1} - command: node admin/server.js admin + image: makeplane/plane-admin:${APP_RELEASE:-v1.2.3} + restart: always depends_on: - api - web @@ -30,8 +30,8 @@ services: - .env live: - image: makeplane/plane-live:${APP_RELEASE:-v0.27.1} - command: node live/dist/server.js live + image: makeplane/plane-live:${APP_RELEASE:-v1.2.3} + restart: always depends_on: - api - web @@ -39,7 +39,8 @@ services: - .env api: - image: makeplane/plane-backend:${APP_RELEASE:-v0.27.1} + image: makeplane/plane-backend:${APP_RELEASE:-v1.2.3} + restart: always command: ./bin/docker-entrypoint-api.sh volumes: - logs_api:/code/plane/logs @@ -51,7 +52,8 @@ services: - .env worker: - image: makeplane/plane-backend:${APP_RELEASE:-v0.27.1} + image: makeplane/plane-backend:${APP_RELEASE:-v1.2.3} + restart: always command: ./bin/docker-entrypoint-worker.sh volumes: - logs_worker:/code/plane/logs @@ -64,7 +66,8 @@ services: - .env beat-worker: - image: makeplane/plane-backend:${APP_RELEASE:-v0.27.1} + image: makeplane/plane-backend:${APP_RELEASE:-v1.2.3} + restart: always command: ./bin/docker-entrypoint-beat.sh volumes: - logs_beat-worker:/code/plane/logs @@ -77,7 +80,8 @@ services: - .env migrator: - image: makeplane/plane-backend:${APP_RELEASE:-v0.27.1} + image: makeplane/plane-backend:${APP_RELEASE:-v1.2.3} + restart: on-failure command: ./bin/docker-entrypoint-migrator.sh volumes: - logs_migrator:/code/plane/logs @@ -88,7 +92,8 @@ services: - .env plane-db: - image: postgres:17-alpine + image: postgres:15.7-alpine + restart: always command: postgres -c 'max_connections=1000' volumes: - pgdata:/var/lib/postgresql/data @@ -96,33 +101,39 @@ services: - .env plane-redis: - image: valkey/valkey:7.2.5-alpine + image: valkey/valkey:7.2.11-alpine + restart: always volumes: - redisdata:/data - env_file: - - .env plane-mq: image: rabbitmq:3.13.6-management-alpine + restart: always volumes: - rabbitmq_data:/var/lib/rabbitmq env_file: - .env - plane-minio: - image: minio/minio:latest - command: server /export --console-address ":9090" + plane-storage: + image: rustfs/rustfs:1.0.0-alpha.90 + restart: always volumes: - - uploads:/export + - uploads:/data env_file: - .env proxy: - image: makeplane/plane-proxy:${APP_RELEASE:-v0.27.1} + image: makeplane/plane-proxy:${APP_RELEASE:-v1.2.3} + restart: always depends_on: - web - api - space + - admin + - live + volumes: + - proxy_config:/config + - proxy_data:/data env_file: - .env @@ -135,3 +146,5 @@ volumes: logs_beat-worker: logs_migrator: rabbitmq_data: + proxy_config: + proxy_data: diff --git a/blueprints/plane/template.toml b/blueprints/plane/template.toml index a12f4564..93607582 100644 --- a/blueprints/plane/template.toml +++ b/blueprints/plane/template.toml @@ -1,53 +1,59 @@ [variables] main_domain = "${domain}" +postgres_user = "${username}" db_password = "${password:32}" -minio_password = "${password:32}" +storage_password = "${password:32}" rabbitmq_user = "${username}" rabbitmq_pass = "${password:32}" secret_key = "${base64:48}" +live_secret_key = "${base64:32}" [config] env = [ -"Domain=${domain}", -"WEB_URL=${Domain}", +"APP_DOMAIN=${main_domain}", +"WEB_URL=http://${main_domain}", "PGHOST=plane-db", "PGDATABASE=plane", -"POSTGRES_USER=${username}", +"POSTGRES_USER=${postgres_user}", "POSTGRES_PASSWORD=${db_password}", "POSTGRES_DB=plane", "POSTGRES_PORT=5432", "PGDATA=/var/lib/postgresql/data", +"DATABASE_URL=postgresql://${postgres_user}:${db_password}@plane-db/plane", "REDIS_HOST=plane-redis", "REDIS_PORT=6379", "REDIS_URL=redis://plane-redis:6379/", -"MINIO_ROOT_USER=access-key", -"MINIO_ROOT_PASSWORD=${minio_password}", +"RUSTFS_ACCESS_KEY=access-key", +"RUSTFS_SECRET_KEY=${storage_password}", +"RUSTFS_VOLUMES=/data", +"RUSTFS_ADDRESS=0.0.0.0:9000", +"RUSTFS_CONSOLE_ENABLE=false", "AWS_REGION=", -"AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER}", -"AWS_SECRET_ACCESS_KEY=${minio_password}", -"AWS_S3_ENDPOINT_URL=http://plane-minio:9000", +"AWS_ACCESS_KEY_ID=access-key", +"AWS_SECRET_ACCESS_KEY=${storage_password}", +"AWS_S3_ENDPOINT_URL=http://plane-storage:9000", "AWS_S3_BUCKET_NAME=uploads", -"NGINX_PORT=80", "BUCKET_NAME=uploads", "FILE_SIZE_LIMIT=5242880", "RABBITMQ_HOST=plane-mq", "RABBITMQ_PORT=5672", -"RABBITMQ_DEFAULT_USER=rabbitmq_user", +"RABBITMQ_USER=${rabbitmq_user}", +"RABBITMQ_PASSWORD=${rabbitmq_pass}", +"RABBITMQ_VHOST=plane", +"RABBITMQ_DEFAULT_USER=${rabbitmq_user}", "RABBITMQ_DEFAULT_PASS=${rabbitmq_pass}", "RABBITMQ_DEFAULT_VHOST=plane", -"RABBITMQ_VHOST=plane", +"AMQP_URL=amqp://${rabbitmq_user}:${rabbitmq_pass}@plane-mq:5672/plane", "API_BASE_URL=http://api:8000", "DEBUG=0", -"SENTRY_DSN=", -"SENTRY_ENVIRONMENT=production", -"CORS_ALLOWED_ORIGINS=https://${Domain}", +"CORS_ALLOWED_ORIGINS=http://${main_domain}", "GUNICORN_WORKERS=1", "USE_MINIO=1", -"DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@plane-db/plane", "SECRET_KEY=${secret_key}", -"AMQP_URL=amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@plane-mq:5672/plane", "API_KEY_RATE_LIMIT=60/minute", -"MINIO_ENDPOINT_SSL=0" +"MINIO_ENDPOINT_SSL=0", +"SITE_ADDRESS=:80", +"LIVE_SERVER_SECRET_KEY=${live_secret_key}" ] mounts = [] diff --git a/blueprints/postiz/docker-compose.yml b/blueprints/postiz/docker-compose.yml index dfbf3ae9..a00832dd 100644 --- a/blueprints/postiz/docker-compose.yml +++ b/blueprints/postiz/docker-compose.yml @@ -13,10 +13,25 @@ services: DATABASE_URL: "postgresql://${DB_USER}:${DB_PASSWORD}@postiz-postgres:5432/${DB_NAME}" REDIS_URL: "redis://postiz-redis:6379" BACKEND_INTERNAL_URL: "http://localhost:3000" + TEMPORAL_ADDRESS: "temporal:7233" IS_GENERAL: "true" + DISABLE_REGISTRATION: "false" + RUN_CRON: "true" + NOT_SECURED: "true" STORAGE_PROVIDER: "local" UPLOAD_DIRECTORY: "/uploads" NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads" + + # === Social Media API Settings === + # These variables should be defined in the Dokploy Environment tab. + # For more configuration options and details, please refer to the official documentation. + FACEBOOK_APP_ID: ${FACEBOOK_APP_ID} + FACEBOOK_APP_SECRET: ${FACEBOOK_APP_SECRET} + LINKEDIN_CLIENT_ID: ${LINKEDIN_CLIENT_ID} + LINKEDIN_CLIENT_SECRET: ${LINKEDIN_CLIENT_SECRET} + X_API_KEY: ${X_API_KEY} + X_API_SECRET: ${X_API_SECRET} + volumes: - postiz-config:/config/ - postiz-uploads:/uploads/ @@ -25,6 +40,8 @@ services: condition: service_healthy postiz-redis: condition: service_healthy + temporal: + condition: service_started postiz-postgres: image: postgres:17-alpine @@ -35,27 +52,80 @@ services: POSTGRES_USER: ${DB_USER} POSTGRES_DB: ${DB_NAME} volumes: - - postiz-postgres-data:/var/lib/postgresql/data + - postgres-volume:/var/lib/postgresql/data healthcheck: - test: pg_isready -U ${DB_USER} -d ${DB_NAME} + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] interval: 10s timeout: 3s retries: 3 + start_period: 10s postiz-redis: image: redis:7.2 restart: always healthcheck: - test: redis-cli ping + test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3 volumes: - postiz-redis-data:/data + temporal-elasticsearch: + image: elasticsearch:7.17.27 + environment: + - cluster.routing.allocation.disk.threshold_enabled=true + - cluster.routing.allocation.disk.watermark.low=512mb + - cluster.routing.allocation.disk.watermark.high=256mb + - cluster.routing.allocation.disk.watermark.flood_stage=128mb + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms256m -Xmx256m + - xpack.security.enabled=false + volumes: + - temporal-es-data:/usr/share/elasticsearch/data + + temporal-postgresql: + image: postgres:16 + environment: + POSTGRES_PASSWORD: ${TEMPORAL_PASSWORD} + POSTGRES_USER: ${TEMPORAL_USER} + volumes: + - temporal-pg-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${TEMPORAL_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + temporal: + image: temporalio/auto-setup:1.28.1 + depends_on: + temporal-postgresql: + condition: service_healthy + temporal-elasticsearch: + condition: service_started + environment: + - DB=postgres12 + - DB_PORT=5432 + - POSTGRES_USER=${TEMPORAL_USER} + - POSTGRES_PWD=${TEMPORAL_PASSWORD} + - POSTGRES_SEEDS=temporal-postgresql + - ENABLE_ES=true + - ES_SEEDS=temporal-elasticsearch + - ES_VERSION=v7 + - TEMPORAL_NAMESPACE=default + healthcheck: + test: ["CMD", "tctl", "--address", "127.0.0.1:7233", "workflow", "list"] + interval: 15s + timeout: 10s + retries: 30 + start_period: 120s + volumes: - postiz-postgres-data: + postgres-volume: postiz-redis-data: postiz-config: - postiz-uploads: \ No newline at end of file + postiz-uploads: + temporal-es-data: + temporal-pg-data: \ No newline at end of file diff --git a/blueprints/postiz/template.toml b/blueprints/postiz/template.toml index 260b77af..4528e909 100644 --- a/blueprints/postiz/template.toml +++ b/blueprints/postiz/template.toml @@ -3,19 +3,29 @@ main_domain = "${domain}" db_password = "${password}" db_user = "postiz" db_name = "postiz" +temporal_password = "${password}" +temporal_user = "temporal" jwt_secret = "${base64:32}" [config] +env = [ + "POSTIZ_HOST=${main_domain}", + "JWT_SECRET=${jwt_secret}", + "DB_USER=${db_user}", + "DB_PASSWORD=${db_password}", + "DB_NAME=${db_name}", + "TEMPORAL_USER=${temporal_user}", + "TEMPORAL_PASSWORD=${temporal_password}", + "FACEBOOK_APP_ID=", + "FACEBOOK_APP_SECRET=", + "LINKEDIN_CLIENT_ID=", + "LINKEDIN_CLIENT_SECRET=", + "X_API_KEY=", + "X_API_SECRET=" +] mounts = [] [[config.domains]] serviceName = "postiz-app" -port = 5_000 +port = 5000 host = "${main_domain}" - -[config.env] -POSTIZ_HOST = "${main_domain}" -DB_PASSWORD = "${db_password}" -DB_USER = "${db_user}" -DB_NAME = "${db_name}" -JWT_SECRET = "${jwt_secret}" diff --git a/blueprints/prefect/docker-compose.yml b/blueprints/prefect/docker-compose.yml new file mode 100644 index 00000000..242936e8 --- /dev/null +++ b/blueprints/prefect/docker-compose.yml @@ -0,0 +1,90 @@ +version: "3.8" + +services: + prefect: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect-postgres: + condition: service_healthy + prefect-redis: + condition: service_healthy + environment: + - PREFECT_API_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:${POSTGRES_PASSWORD}@prefect-postgres:5432/prefect + - PREFECT_SERVER_API_HOST=0.0.0.0 + - PREFECT_SERVER_UI_API_URL=https://${PREFECT_DOMAIN}/api + - PREFECT_MESSAGING_BROKER=prefect_redis.messaging + - PREFECT_MESSAGING_CACHE=prefect_redis.messaging + - PREFECT_REDIS_MESSAGING_HOST=prefect-redis + - PREFECT_REDIS_MESSAGING_PORT=6379 + - PREFECT_REDIS_MESSAGING_DB=0 + command: prefect server start --no-services + expose: + - 4200 + healthcheck: + test: + [ + "CMD", + "python", + "-c", + "import urllib.request as u; u.urlopen('http://localhost:4200/api/health', timeout=1)", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + + prefect-services: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect: + condition: service_healthy + environment: + - PREFECT_API_DATABASE_CONNECTION_URL=postgresql+asyncpg://prefect:${POSTGRES_PASSWORD}@prefect-postgres:5432/prefect + - PREFECT_MESSAGING_BROKER=prefect_redis.messaging + - PREFECT_MESSAGING_CACHE=prefect_redis.messaging + - PREFECT_REDIS_MESSAGING_HOST=prefect-redis + - PREFECT_REDIS_MESSAGING_PORT=6379 + - PREFECT_REDIS_MESSAGING_DB=0 + command: prefect server services start + + prefect-worker: + image: prefecthq/prefect:3.7.5-python3.12 + restart: unless-stopped + depends_on: + prefect: + condition: service_healthy + environment: + - PREFECT_API_URL=http://prefect:4200/api + command: prefect worker start --pool local-pool + + prefect-postgres: + image: postgres:14-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=prefect + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=prefect + volumes: + - prefect-postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U prefect -d prefect"] + interval: 5s + timeout: 5s + retries: 5 + + prefect-redis: + image: redis:7.2-alpine + restart: unless-stopped + volumes: + - prefect-redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + prefect-postgres-data: + prefect-redis-data: diff --git a/blueprints/prefect/prefect.svg b/blueprints/prefect/prefect.svg new file mode 100644 index 00000000..07bf5b43 --- /dev/null +++ b/blueprints/prefect/prefect.svg @@ -0,0 +1,7 @@ + + Prefect + Prefect template icon + + + + diff --git a/blueprints/prefect/template.toml b/blueprints/prefect/template.toml new file mode 100644 index 00000000..15f166e5 --- /dev/null +++ b/blueprints/prefect/template.toml @@ -0,0 +1,15 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" + +[config] +env = [ + "PREFECT_DOMAIN=${main_domain}", + "POSTGRES_PASSWORD=${postgres_password}", +] +mounts = [] + +[[config.domains]] +serviceName = "prefect" +port = 4200 +host = "${main_domain}" diff --git a/blueprints/privatebin/docker-compose.yml b/blueprints/privatebin/docker-compose.yml new file mode 100644 index 00000000..dc621ca0 --- /dev/null +++ b/blueprints/privatebin/docker-compose.yml @@ -0,0 +1,11 @@ +services: + privatebin: + image: privatebin/nginx-fpm-alpine:2.0.4 + restart: unless-stopped + volumes: + - privatebin_data:/srv/data + ports: + - "8080" + +volumes: + privatebin_data: diff --git a/blueprints/privatebin/privatebin.svg b/blueprints/privatebin/privatebin.svg new file mode 100644 index 00000000..0391799d --- /dev/null +++ b/blueprints/privatebin/privatebin.svg @@ -0,0 +1,5 @@ + + + + P + diff --git a/blueprints/privatebin/template.toml b/blueprints/privatebin/template.toml new file mode 100644 index 00000000..747c3bc7 --- /dev/null +++ b/blueprints/privatebin/template.toml @@ -0,0 +1,11 @@ +[variables] +main_domain = "${domain}" + +[config] +env = {} +mounts = [] + +[[config.domains]] +serviceName = "privatebin" +port = 8080 +host = "${main_domain}" diff --git a/blueprints/pyrodactyl/pyrodactyl.png b/blueprints/pyrodactyl/pyrodactyl.png index 50c98fdd..81513193 100644 Binary files a/blueprints/pyrodactyl/pyrodactyl.png and b/blueprints/pyrodactyl/pyrodactyl.png differ diff --git a/blueprints/qui/docker-compose.yml b/blueprints/qui/docker-compose.yml new file mode 100644 index 00000000..0158d9cd --- /dev/null +++ b/blueprints/qui/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3.8" + +services: + qui: + image: ghcr.io/autobrr/qui:latest + restart: unless-stopped + expose: + - "7476" + volumes: + - qui-config:/config + +volumes: + qui-config: diff --git a/blueprints/qui/qui.png b/blueprints/qui/qui.png new file mode 100644 index 00000000..64897dc4 Binary files /dev/null and b/blueprints/qui/qui.png differ diff --git a/blueprints/qui/template.toml b/blueprints/qui/template.toml new file mode 100644 index 00000000..3858b997 --- /dev/null +++ b/blueprints/qui/template.toml @@ -0,0 +1,9 @@ +[variables] +main_domain = "${domain}" + +[config] +[[config.domains]] +serviceName = "qui" +port = 7476 +host = "${main_domain}" +path = "/" diff --git a/blueprints/rallly/docker-compose.yml b/blueprints/rallly/docker-compose.yml new file mode 100644 index 00000000..ac6badae --- /dev/null +++ b/blueprints/rallly/docker-compose.yml @@ -0,0 +1,66 @@ +version: "3.8" + +services: + rallly: + image: lukevella/rallly:4.11.1 + restart: unless-stopped + depends_on: + - db + - minio + - minio-init + environment: + NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL} + DATABASE_URL: ${DATABASE_URL} + SECRET_PASSWORD: ${SECRET_PASSWORD} + SUPPORT_EMAIL: ${SUPPORT_EMAIL} + NOREPLY_EMAIL: ${NOREPLY_EMAIL} + SMTP_HOST: ${SMTP_HOST} + SMTP_PORT: ${SMTP_PORT} + SMTP_USER: ${SMTP_USER} + SMTP_PWD: ${SMTP_PWD} + SMTP_SECURE: ${SMTP_SECURE} + S3_ENDPOINT: ${S3_ENDPOINT} + S3_BUCKET_NAME: ${S3_BUCKET_NAME} + S3_REGION: ${S3_REGION} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} + INITIAL_ADMIN_EMAIL: ${INITIAL_ADMIN_EMAIL} + + db: + image: postgres:14-alpine + restart: unless-stopped + environment: + POSTGRES_DB: rallly + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - rallly-db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d rallly"] + interval: 10s + timeout: 5s + retries: 5 + + minio: + image: minio/minio:latest + restart: unless-stopped + command: server /data + environment: + MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID} + MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY} + volumes: + - rallly-minio:/data + + minio-init: + image: minio/mc:latest + depends_on: + - minio + restart: "no" + entrypoint: > + /bin/sh -c " + until mc alias set local http://minio:9000 ${S3_ACCESS_KEY_ID} ${S3_SECRET_ACCESS_KEY}; do sleep 2; done; + mc mb --ignore-existing local/rallly; + " + +volumes: + rallly-db: + rallly-minio: \ No newline at end of file diff --git a/blueprints/rallly/rallly.svg b/blueprints/rallly/rallly.svg new file mode 100644 index 00000000..b4e44b30 --- /dev/null +++ b/blueprints/rallly/rallly.svg @@ -0,0 +1,8 @@ + + Rallly + + + + + + \ No newline at end of file diff --git a/blueprints/rallly/template.toml b/blueprints/rallly/template.toml new file mode 100644 index 00000000..902a72f8 --- /dev/null +++ b/blueprints/rallly/template.toml @@ -0,0 +1,40 @@ +[variables] +main_domain = "${domain}" +support_email = "${email}" +noreply_email = "${email}" +initial_admin_email = "${email}" +postgres_password = "${password:32}" +secret_password = "${password:32}" +s3_access_key_id = "rallly" +s3_secret_access_key = "${password:32}" +smtp_host = "smtp.example.com" +smtp_port = "587" +smtp_user = "change-me" +smtp_password = "${password:32}" +smtp_secure = "false" + +[config] +env = [ + "NEXT_PUBLIC_BASE_URL=https://${main_domain}", + "DATABASE_URL=postgres://postgres:${postgres_password}@db:5432/rallly", + "SECRET_PASSWORD=${secret_password}", + "SUPPORT_EMAIL=${support_email}", + "NOREPLY_EMAIL=${noreply_email}", + "SMTP_HOST=${smtp_host}", + "SMTP_PORT=${smtp_port}", + "SMTP_USER=${smtp_user}", + "SMTP_PWD=${smtp_password}", + "SMTP_SECURE=${smtp_secure}", + "S3_ENDPOINT=http://minio:9000", + "S3_BUCKET_NAME=rallly", + "S3_REGION=us-east-1", + "S3_ACCESS_KEY_ID=${s3_access_key_id}", + "S3_SECRET_ACCESS_KEY=${s3_secret_access_key}", + "INITIAL_ADMIN_EMAIL=${initial_admin_email}", + "POSTGRES_PASSWORD=${postgres_password}", +] + +[[config.domains]] +serviceName = "rallly" +port = 3000 +host = "${main_domain}" \ No newline at end of file diff --git a/blueprints/rustrak-full/docker-compose.yml b/blueprints/rustrak-full/docker-compose.yml new file mode 100644 index 00000000..16f734c7 --- /dev/null +++ b/blueprints/rustrak-full/docker-compose.yml @@ -0,0 +1,49 @@ +version: "3.8" + +services: + rustrak-server: + image: abians7/rustrak-server:v0.2.1-postgres + restart: unless-stopped + environment: + - HOST=0.0.0.0 + - PORT=8080 + - RUST_LOG=info + - SSL_PROXY=true + - DATABASE_URL=postgres://rustrak:${POSTGRES_PASSWORD}@postgres:5432/rustrak + - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} + - CREATE_SUPERUSER=${CREATE_SUPERUSER} + ports: + - 8080 + depends_on: + postgres: + condition: service_healthy + + rustrak-ui: + image: abians7/rustrak-ui:v0.1.6 + restart: unless-stopped + environment: + - RUSTRAK_API_URL=${RUSTRAK_API_URL} + ports: + - 3000 + depends_on: + - rustrak-server + + postgres: + image: postgres:16-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=rustrak + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=rustrak + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U rustrak -d rustrak"] + interval: 10s + timeout: 5s + retries: 5 + expose: + - 5432 + +volumes: + postgres_data: diff --git a/blueprints/rustrak-full/rustrak.svg b/blueprints/rustrak-full/rustrak.svg new file mode 100644 index 00000000..11d87e47 --- /dev/null +++ b/blueprints/rustrak-full/rustrak.svg @@ -0,0 +1,6 @@ + + + + diff --git a/blueprints/rustrak-full/template.toml b/blueprints/rustrak-full/template.toml new file mode 100644 index 00000000..383f486f --- /dev/null +++ b/blueprints/rustrak-full/template.toml @@ -0,0 +1,27 @@ +[variables] +main_domain = "${domain}" +api_domain = "${domain}" +postgres_password = "${password:32}" +session_secret_key = "${password:64}" +admin_email = "${email}" +admin_password = "${password:16}" +create_superuser = "${admin_email}:${admin_password}" +rustrak_api_url = "https://${api_domain}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "SESSION_SECRET_KEY=${session_secret_key}", + "CREATE_SUPERUSER=${create_superuser}", + "RUSTRAK_API_URL=${rustrak_api_url}", +] + +[[config.domains]] +serviceName = "rustrak-ui" +port = 3000 +host = "${main_domain}" + +[[config.domains]] +serviceName = "rustrak-server" +port = 8080 +host = "${api_domain}" diff --git a/blueprints/rustrak/docker-compose.yml b/blueprints/rustrak/docker-compose.yml new file mode 100644 index 00000000..e4c52b0f --- /dev/null +++ b/blueprints/rustrak/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.8" + +services: + rustrak: + image: abians7/rustrak-server:v0.2.1 + restart: unless-stopped + environment: + - HOST=0.0.0.0 + - PORT=8080 + - RUST_LOG=info + - SSL_PROXY=true + - SESSION_SECRET_KEY=${SESSION_SECRET_KEY} + - CREATE_SUPERUSER=${CREATE_SUPERUSER} + volumes: + - rustrak_data:/data + ports: + - 8080 + +volumes: + rustrak_data: diff --git a/blueprints/rustrak/rustrak.svg b/blueprints/rustrak/rustrak.svg new file mode 100644 index 00000000..11d87e47 --- /dev/null +++ b/blueprints/rustrak/rustrak.svg @@ -0,0 +1,6 @@ + + + + diff --git a/blueprints/rustrak/template.toml b/blueprints/rustrak/template.toml new file mode 100644 index 00000000..819f6f85 --- /dev/null +++ b/blueprints/rustrak/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +session_secret_key = "${password:64}" +admin_email = "${email}" +admin_password = "${password:16}" +create_superuser = "${admin_email}:${admin_password}" + +[config] +env = [ + "SESSION_SECRET_KEY=${session_secret_key}", + "CREATE_SUPERUSER=${create_superuser}", +] + +[[config.domains]] +serviceName = "rustrak" +port = 8080 +host = "${main_domain}" diff --git a/blueprints/seanime/docker-compose.yml b/blueprints/seanime/docker-compose.yml new file mode 100644 index 00000000..9f6992a0 --- /dev/null +++ b/blueprints/seanime/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.8" +services: + seanime-init: + image: busybox:1.37.0 + user: "0:0" + volumes: + - ./seanime-config:/mnt/config + - ./anime:/mnt/anime + - ./downloads:/mnt/downloads + command: + [ "sh", "-c", "chown -R 1000:1000 /mnt/config && find /mnt/config -type d -exec chmod 755 {} + && find /mnt/config -type f -exec chmod 644 {} +",] + + seanime: + image: umagistr/seanime:v3.6.1-rootless + depends_on: + seanime-init: + condition: service_completed_successfully + environment: + - SEANIME_SERVER_HOST=0.0.0.0 + expose: + - "43211" + volumes: + - ./seanime-config:/home/seanime/.config/Seanime + - ./anime:/anime + - ./downloads:/downloads + restart: unless-stopped \ No newline at end of file diff --git a/blueprints/seanime/seanime.png b/blueprints/seanime/seanime.png new file mode 100644 index 00000000..9e2459d9 Binary files /dev/null and b/blueprints/seanime/seanime.png differ diff --git a/blueprints/seanime/template.toml b/blueprints/seanime/template.toml new file mode 100644 index 00000000..eabd94ef --- /dev/null +++ b/blueprints/seanime/template.toml @@ -0,0 +1,7 @@ +[variables] +main_domain = "${domain}" + +[[config.domains]] +serviceName = "seanime" +port = 43211 +host = "${main_domain}" \ No newline at end of file diff --git a/blueprints/senddock/docker-compose.yml b/blueprints/senddock/docker-compose.yml new file mode 100644 index 00000000..c4ff03d0 --- /dev/null +++ b/blueprints/senddock/docker-compose.yml @@ -0,0 +1,57 @@ +version: "3.8" +services: + postgres: + image: postgres:17-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=senddock + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=senddock + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U senddock"] + interval: 10s + timeout: 5s + retries: 6 + + redis: + image: redis:7-alpine + restart: unless-stopped + volumes: + - redisdata:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + + senddock: + image: ghcr.io/arkhe-systems/senddock:0.6.5.1 + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + environment: + - DATABASE_URL=postgres://senddock:${POSTGRES_PASSWORD}@postgres:5432/senddock?sslmode=disable + - REDIS_URL=redis://redis:6379 + - JWT_SECRET=${JWT_SECRET} + - PUBLIC_URL=${PUBLIC_URL} + - FRONTEND_URL=${PUBLIC_URL} + - DEPLOYMENT_MODE=self-hosted + - SENDDOCK_LICENSE_KEY=${SENDDOCK_LICENSE_KEY} + - PORT=8080 + expose: + - 8080 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 15s + +volumes: + pgdata: + redisdata: diff --git a/blueprints/senddock/senddock.svg b/blueprints/senddock/senddock.svg new file mode 100644 index 00000000..9dc916eb --- /dev/null +++ b/blueprints/senddock/senddock.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/blueprints/senddock/template.toml b/blueprints/senddock/template.toml new file mode 100644 index 00000000..4a8c456e --- /dev/null +++ b/blueprints/senddock/template.toml @@ -0,0 +1,17 @@ +[variables] +main_domain = "${domain}" +postgres_password = "${password:32}" +jwt_secret = "${password:64}" + +[config] +env = [ + "POSTGRES_PASSWORD=${postgres_password}", + "JWT_SECRET=${jwt_secret}", + "PUBLIC_URL=https://${main_domain}", + "SENDDOCK_LICENSE_KEY=" +] + +[[config.domains]] +serviceName = "senddock" +port = 8080 +host = "${main_domain}" diff --git a/blueprints/seq/README.md b/blueprints/seq/README.md new file mode 100644 index 00000000..d7c35632 --- /dev/null +++ b/blueprints/seq/README.md @@ -0,0 +1,13 @@ +# Seq + +Seq is a self-hosted log search and structured observability server from Datalust. + +This Dokploy template includes: + +- Seq server using the official Datalust container image +- Generated first-run admin password +- Canonical URL configured from the Dokploy domain +- Persistent `/data` volume +- Dokploy domain routing for the web UI and ingestion API on port 80 + +The template sets `ACCEPT_EULA=Y`, which is required by the official Seq container image. \ No newline at end of file diff --git a/blueprints/seq/docker-compose.yml b/blueprints/seq/docker-compose.yml new file mode 100644 index 00000000..2f9aa3de --- /dev/null +++ b/blueprints/seq/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" + +services: + seq: + image: datalust/seq:2025.2 + restart: unless-stopped + environment: + ACCEPT_EULA: "Y" + SEQ_API_CANONICALURI: ${SEQ_API_CANONICALURI} + SEQ_FIRSTRUN_ADMINPASSWORD: ${SEQ_FIRSTRUN_ADMINPASSWORD} + expose: + - "80" + volumes: + - seq-data:/data + +volumes: + seq-data: {} \ No newline at end of file diff --git a/blueprints/seq/logo.svg b/blueprints/seq/logo.svg new file mode 100644 index 00000000..edc1a7f4 --- /dev/null +++ b/blueprints/seq/logo.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/blueprints/seq/template.toml b/blueprints/seq/template.toml new file mode 100644 index 00000000..73543877 --- /dev/null +++ b/blueprints/seq/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +admin_password = "${password:32}" + +[config] +env = [ + "ACCEPT_EULA=Y", + "SEQ_API_CANONICALURI=https://${main_domain}", + "SEQ_FIRSTRUN_ADMINPASSWORD=${admin_password}" +] +mounts = [] + +[[config.domains]] +serviceName = "seq" +port = 80 +host = "${main_domain}" \ No newline at end of file diff --git a/blueprints/snipe-it/docker-compose.yml b/blueprints/snipe-it/docker-compose.yml new file mode 100644 index 00000000..be3140b7 --- /dev/null +++ b/blueprints/snipe-it/docker-compose.yml @@ -0,0 +1,68 @@ +version: "3.8" + +services: + snipe-it: + image: snipe/snipe-it:v8.5.0 + restart: unless-stopped + expose: + - 80 + volumes: + - snipe-it-storage:/var/lib/snipeit + environment: + - APP_ENV=production + - APP_DEBUG=false + - APP_KEY=${APP_KEY} + - APP_URL=${APP_URL} + - APP_TIMEZONE=${APP_TIMEZONE} + - APP_LOCALE=${APP_LOCALE} + - APP_TRUSTED_PROXIES=${APP_TRUSTED_PROXIES} + - APP_FORCE_TLS=${APP_FORCE_TLS} + - SECURE_COOKIES=${SECURE_COOKIES} + - PRIVATE_FILESYSTEM_DISK=local + - PUBLIC_FILESYSTEM_DISK=local_public + - DB_CONNECTION=mysql + - DB_HOST=snipe-it-db + - DB_PORT=3306 + - DB_DATABASE=${DB_DATABASE} + - DB_USERNAME=${DB_USERNAME} + - DB_PASSWORD=${DB_PASSWORD} + - DB_PREFIX=null + - DB_DUMP_PATH=/usr/bin + - DB_DUMP_SKIP_SSL=true + - DB_CHARSET=utf8mb4 + - DB_COLLATION=utf8mb4_unicode_ci + - MAIL_MAILER=log + - MAIL_FROM_ADDR=${MAIL_FROM_ADDR} + - MAIL_FROM_NAME=Snipe-IT + - MAIL_REPLYTO_ADDR=${MAIL_FROM_ADDR} + - MAIL_REPLYTO_NAME=Snipe-IT + - ALLOW_BACKUP_DELETE=false + - ALLOW_DATA_PURGE=false + - IMAGE_LIB=gd + - LOG_CHANNEL=stderr + - CACHE_DRIVER=file + - SESSION_DRIVER=file + - QUEUE_DRIVER=sync + depends_on: + snipe-it-db: + condition: service_healthy + + snipe-it-db: + image: mariadb:11.4.7 + restart: unless-stopped + volumes: + - snipe-it-db-data:/var/lib/mysql + environment: + - MYSQL_DATABASE=${DB_DATABASE} + - MYSQL_USER=${DB_USERNAME} + - MYSQL_PASSWORD=${DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 5s + timeout: 2s + retries: 10 + +volumes: + snipe-it-storage: + snipe-it-db-data: diff --git a/blueprints/snipe-it/snipe-it.png b/blueprints/snipe-it/snipe-it.png new file mode 100644 index 00000000..423a5b74 Binary files /dev/null and b/blueprints/snipe-it/snipe-it.png differ diff --git a/blueprints/snipe-it/template.toml b/blueprints/snipe-it/template.toml new file mode 100644 index 00000000..aca258a5 --- /dev/null +++ b/blueprints/snipe-it/template.toml @@ -0,0 +1,25 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" +db_password = "${password:24}" +mysql_root_password = "${password:24}" + +[config] +[[config.domains]] +serviceName = "snipe-it" +port = 80 +host = "${main_domain}" + +[config.env] +APP_KEY = "base64:${app_key}" +APP_URL = "https://${main_domain}" +APP_TIMEZONE = "UTC" +APP_LOCALE = "en-US" +APP_TRUSTED_PROXIES = "172.0.0.0/8" +APP_FORCE_TLS = "true" +SECURE_COOKIES = "true" +DB_DATABASE = "snipeit" +DB_USERNAME = "snipeit" +DB_PASSWORD = "${db_password}" +MYSQL_ROOT_PASSWORD = "${mysql_root_password}" +MAIL_FROM_ADDR = "snipe-it@${main_domain}" diff --git a/blueprints/soft-serve/docker-compose.yml b/blueprints/soft-serve/docker-compose.yml new file mode 100644 index 00000000..ac875d21 --- /dev/null +++ b/blueprints/soft-serve/docker-compose.yml @@ -0,0 +1,27 @@ +version: "3.8" + +services: + soft-serve: + image: charmcli/soft-serve:v0.11.6 + restart: unless-stopped + expose: + - "23231" + - "23232" + - "23233" + - "9418" + environment: + SOFT_SERVE_NAME: ${SOFT_SERVE_NAME} + SOFT_SERVE_DATA_PATH: /soft-serve + SOFT_SERVE_INITIAL_ADMIN_KEYS: ${SOFT_SERVE_INITIAL_ADMIN_KEYS} + SOFT_SERVE_SSH_LISTEN_ADDR: ":23231" + SOFT_SERVE_SSH_PUBLIC_URL: ${SOFT_SERVE_SSH_PUBLIC_URL} + SOFT_SERVE_HTTP_LISTEN_ADDR: ":23232" + SOFT_SERVE_HTTP_PUBLIC_URL: ${SOFT_SERVE_HTTP_PUBLIC_URL} + SOFT_SERVE_GIT_LISTEN_ADDR: ":9418" + SOFT_SERVE_STATS_LISTEN_ADDR: ":23233" + SOFT_SERVE_LOG_LEVEL: ${SOFT_SERVE_LOG_LEVEL} + volumes: + - soft-serve-data:/soft-serve + +volumes: + soft-serve-data: diff --git a/blueprints/soft-serve/soft-serve.svg b/blueprints/soft-serve/soft-serve.svg new file mode 100644 index 00000000..0bb41d55 --- /dev/null +++ b/blueprints/soft-serve/soft-serve.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/blueprints/soft-serve/template.toml b/blueprints/soft-serve/template.toml new file mode 100644 index 00000000..f18f883c --- /dev/null +++ b/blueprints/soft-serve/template.toml @@ -0,0 +1,62 @@ +[variables] +main_domain = "${domain}" +server_name = "Soft Serve" +initial_admin_keys = "" +http_public_url = "http://${main_domain}" +ssh_public_url = "ssh://${main_domain}:23231" +log_level = "info" + +[[config.domains]] +serviceName = "soft-serve" +port = 23232 +host = "${main_domain}" +path = "/" + +[[config.mounts]] +filePath = "README.md" +content = """# Soft Serve + +This template deploys Soft Serve, a self-hosted Git server with an SSH TUI, HTTP Git access, Git LFS support, and a lightweight web endpoint. + +## First Start + +Set `initial_admin_keys` to one or more SSH public keys before the first deployment. Soft Serve uses this value to create the initial `admin` user. The keys are consumed during initialization, so changing this variable later does not rewrite an already initialized data volume. + +Example value: + +```text +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... your-name +``` + +## HTTP Access + +The Dokploy domain routes to Soft Serve's HTTP listener: + +- HTTP public URL: `http://${main_domain}` +- HTTP Git clone example: `git clone http://${main_domain}/repo.git` + +Set `http_public_url` to `https://${main_domain}` before deploying if your Dokploy domain enforces TLS. + +Private HTTP access uses tokens created through the SSH admin interface. + +## SSH and Git Protocol Ports + +Soft Serve also listens inside the deployment on: + +- SSH/TUI: `23231` +- Stats: `23233` +- Git protocol: `9418` + +This template does not publish host TCP ports directly. Expose the SSH or Git protocol ports separately in Dokploy if you need direct terminal/TUI access, then use the configured `ssh_public_url` for clone commands. + +## Storage + +Repositories, configuration, keys, and the default SQLite database are stored in the persistent `soft-serve-data` volume mounted at `/soft-serve`. +""" + +[config.env] +SOFT_SERVE_NAME = "${server_name}" +SOFT_SERVE_INITIAL_ADMIN_KEYS = "${initial_admin_keys}" +SOFT_SERVE_HTTP_PUBLIC_URL = "${http_public_url}" +SOFT_SERVE_SSH_PUBLIC_URL = "${ssh_public_url}" +SOFT_SERVE_LOG_LEVEL = "${log_level}" diff --git a/blueprints/speedtest-tracker/docker-compose.yml b/blueprints/speedtest-tracker/docker-compose.yml new file mode 100644 index 00000000..234eddb4 --- /dev/null +++ b/blueprints/speedtest-tracker/docker-compose.yml @@ -0,0 +1,16 @@ +services: + speedtest-tracker: + image: lscr.io/linuxserver/speedtest-tracker:latest + restart: unless-stopped + environment: + PUID: ${PUID} + PGID: ${PGID} + APP_KEY: ${APP_KEY} + DB_CONNECTION: sqlite + volumes: + - speedtest_config:/config + ports: + - "80" + +volumes: + speedtest_config: diff --git a/blueprints/speedtest-tracker/speedtest-tracker.svg b/blueprints/speedtest-tracker/speedtest-tracker.svg new file mode 100644 index 00000000..1928bbb7 --- /dev/null +++ b/blueprints/speedtest-tracker/speedtest-tracker.svg @@ -0,0 +1,5 @@ + + + + S + diff --git a/blueprints/speedtest-tracker/template.toml b/blueprints/speedtest-tracker/template.toml new file mode 100644 index 00000000..2ae03d8c --- /dev/null +++ b/blueprints/speedtest-tracker/template.toml @@ -0,0 +1,16 @@ +[variables] +main_domain = "${domain}" +app_key = "${base64:32}" + +[config] +mounts = [] + +[[config.domains]] +serviceName = "speedtest-tracker" +port = 80 +host = "${main_domain}" + +[config.env] +APP_KEY = "base64:${app_key}" +PUID = "1000" +PGID = "1000" diff --git a/blueprints/supabase/docker-compose.yml b/blueprints/supabase/docker-compose.yml index 506ddc90..3e03fb2c 100644 --- a/blueprints/supabase/docker-compose.yml +++ b/blueprints/supabase/docker-compose.yml @@ -1,89 +1,107 @@ # Usage -# Start: docker compose up -# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up +# Start: docker compose up -d # Stop: docker compose down -# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans -# Reset everything: ./reset.sh +# Reset everything: ./reset.sh +# +# Dokploy: Kong is exposed on port 8000 for domain routing. Storage uses local file +# backend by default; edit the storage service env in this file to switch to S3/MinIO. name: supabase services: studio: - container_name: ${CONTAINER_PREFIX}-studio - image: supabase/studio:2025.04.21-sha-173cc56 + image: supabase/studio:2026.04.27-sha-5f60601 restart: unless-stopped healthcheck: test: [ - "CMD", - "node", - "-e", - "fetch('http://studio:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})" + "CMD-SHELL", + "node -e \"fetch('http://localhost:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})\"" ] timeout: 10s interval: 5s - retries: 3 + retries: 5 + start_period: 30s depends_on: analytics: condition: service_healthy environment: + HOSTNAME: "0.0.0.0" + STUDIO_PG_META_URL: http://meta:8080 + POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} + POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY} + PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} + PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} + DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION} DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT} OPENAI_API_KEY: ${OPENAI_API_KEY:-} - SUPABASE_URL: ${SUPABASE_PUBLIC_URL} + SUPABASE_URL: http://kong:8000 SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} AUTH_JWT_SECRET: ${JWT_SECRET} - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_API_KEY: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + LOGFLARE_URL: http://analytics:4000 - NEXT_PUBLIC_ENABLE_LOGS: true - # Comment to use Big Query backend for analytics + NEXT_PUBLIC_ENABLE_LOGS: "true" NEXT_ANALYTICS_BACKEND_PROVIDER: postgres - # Uncomment to use Big Query backend for analytics - # NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery + SNIPPETS_MANAGEMENT_FOLDER: /app/snippets + EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions + volumes: + - ../files/volumes/snippets:/app/snippets:Z + - ../files/volumes/functions:/app/edge-functions:Z kong: - container_name: ${CONTAINER_PREFIX}-kong - image: kong:2.8.1 + image: kong/kong:3.9.1 restart: unless-stopped - # ports: - # - ${KONG_HTTP_PORT}:8000/tcp - # - ${KONG_HTTPS_PORT}:8443/tcp + healthcheck: + test: ["CMD", "kong", "health"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 30s + depends_on: + studio: + condition: service_healthy expose: - 8000 - 8443 volumes: - # https://github.com/supabase/supabase/issues/12661 - ../files/volumes/api/kong.yml:/home/kong/temp.yml:ro,z - depends_on: - analytics: - condition: service_healthy + - ../files/volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z environment: KONG_DATABASE: "off" - KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml - # https://github.com/supabase/cli/issues/14 + KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml KONG_DNS_ORDER: LAST,A,CNAME - KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth + KONG_DNS_NOT_FOUND_TTL: 1 + KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + KONG_PROXY_ACCESS_LOG: /dev/stdout combined SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-} + SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-} + ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-} + SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-} DASHBOARD_USERNAME: ${DASHBOARD_USERNAME} DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD} - CONTAINER_PREFIX: ${CONTAINER_PREFIX} - # https://unix.stackexchange.com/a/294837 - entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start' + entrypoint: ["/bin/bash", "/home/kong/kong-entrypoint.sh"] auth: - container_name: ${CONTAINER_PREFIX}-auth - image: supabase/gotrue:v2.171.0 + image: supabase/gotrue:v2.186.0 restart: unless-stopped healthcheck: test: @@ -100,12 +118,8 @@ services: retries: 3 depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: - # the next line seems required if you want to be able to send mails from the supabase GUI GOTRUE_MAILER_EXTERNAL_HOSTS: kong,${SUPABASE_HOST} GOTRUE_API_HOST: 0.0.0.0 GOTRUE_API_PORT: 9999 @@ -123,16 +137,12 @@ services: GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated GOTRUE_JWT_EXP: ${JWT_EXPIRY} GOTRUE_JWT_SECRET: ${JWT_SECRET} + GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]} GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM} - # Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile. - # GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true - - # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true - # GOTRUE_SMTP_MAX_FREQUENCY: 1s GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL} GOTRUE_SMTP_HOST: ${SMTP_HOST} GOTRUE_SMTP_PORT: ${SMTP_PORT} @@ -146,39 +156,18 @@ services: GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP} GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM} - # Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook - - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true" - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook" - # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "" - - # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true" - # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt" - - # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true" - # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt" - - # GOTRUE_HOOK_SEND_SMS_ENABLED: "false" - # GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook" - # GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" - - # GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false" - # GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender" - # GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n" rest: - container_name: ${CONTAINER_PREFIX}-rest - image: postgrest/postgrest:v12.2.11 + image: postgrest/postgrest:v14.8 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} + PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} + PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} PGRST_DB_ANON_ROLE: anon PGRST_JWT_SECRET: ${JWT_SECRET} PGRST_DB_USE_LEGACY_GUCS: "false" @@ -190,32 +179,21 @@ services: ] realtime: - # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain - container_name: realtime-dev.${CONTAINER_PREFIX}-realtime - image: supabase/realtime:v2.34.47 + image: supabase/realtime:v2.76.5 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy healthcheck: test: [ - "CMD", - "curl", - "-sSfL", - "--head", - "-o", - "/dev/null", - "-H", - "Authorization: Bearer ${ANON_KEY}", - "http://localhost:4000/api/tenants/realtime-dev/health" + "CMD-SHELL", + "curl -sSfL --head -o /dev/null -H \"Authorization: Bearer ${ANON_KEY}\" http://localhost:4000/api/tenants/realtime-dev/health" ] timeout: 5s - interval: 5s + interval: 30s retries: 3 + start_period: 10s environment: PORT: 4000 DB_HOST: ${POSTGRES_HOST} @@ -227,20 +205,25 @@ services: DB_ENC_KEY: supabaserealtime API_JWT_SECRET: ${JWT_SECRET} SECRET_KEY_BASE: ${SECRET_KEY_BASE} + METRICS_JWT_SECRET: ${JWT_SECRET} ERL_AFLAGS: -proto_dist inet_tcp DNS_NODES: "''" RLIMIT_NOFILE: "10000" APP_NAME: realtime - SEED_SELF_HOST: true - RUN_JANITOR: true + SEED_SELF_HOST: "true" + RUN_JANITOR: "true" + DISABLE_HEALTHCHECK_LOGGING: "true" - # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up storage: - container_name: ${CONTAINER_PREFIX}-storage - image: supabase/storage-api:v1.22.7 + image: supabase/storage-api:v1.48.26 restart: unless-stopped - volumes: - - ../files/volumes/storage:/var/lib/storage:z + depends_on: + db: + condition: service_healthy + rest: + condition: service_started + imgproxy: + condition: service_started healthcheck: test: [ @@ -254,33 +237,30 @@ services: timeout: 5s interval: 5s retries: 3 - depends_on: - db: - # Disable this if you are using an external Postgres database - condition: service_healthy - rest: - condition: service_started - imgproxy: - condition: service_started + start_period: 10s + volumes: + - ../files/volumes/storage:/var/lib/storage:z environment: ANON_KEY: ${ANON_KEY} SERVICE_KEY: ${SERVICE_ROLE_KEY} POSTGREST_URL: http://rest:3000 - PGRST_JWT_SECRET: ${JWT_SECRET} + AUTH_JWT_SECRET: ${JWT_SECRET} DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} + STORAGE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} + REQUEST_ALLOW_X_FORWARDED_PATH: "true" FILE_SIZE_LIMIT: 52428800 STORAGE_BACKEND: file + GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET} FILE_STORAGE_BACKEND_PATH: /var/lib/storage - TENANT_ID: stub - # TODO: https://github.com/supabase/storage-api/issues/55 - REGION: stub - GLOBAL_S3_BUCKET: stub + TENANT_ID: ${STORAGE_TENANT_ID} + REGION: ${REGION} ENABLE_IMAGE_TRANSFORMATION: "true" IMGPROXY_URL: http://imgproxy:5001 + S3_PROTOCOL_ACCESS_KEY_ID: ${S3_PROTOCOL_ACCESS_KEY_ID} + S3_PROTOCOL_ACCESS_KEY_SECRET: ${S3_PROTOCOL_ACCESS_KEY_SECRET} imgproxy: - container_name: ${CONTAINER_PREFIX}-imgproxy - image: darthsim/imgproxy:v3.8.0 + image: darthsim/imgproxy:v3.30.1 restart: unless-stopped volumes: - ../files/volumes/storage:/var/lib/storage:z @@ -298,17 +278,14 @@ services: IMGPROXY_BIND: ":5001" IMGPROXY_LOCAL_FILESYSTEM_ROOT: / IMGPROXY_USE_ETAG: "true" - IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION} + IMGPROXY_AUTO_WEBP: ${IMGPROXY_AUTO_WEBP} + IMGPROXY_MAX_SRC_RESOLUTION: 16.8 meta: - container_name: ${CONTAINER_PREFIX}-meta - image: supabase/postgres-meta:v0.88.9 + image: supabase/postgres-meta:v0.96.3 restart: unless-stopped depends_on: db: - # Disable this if you are using an external Postgres database - condition: service_healthy - analytics: condition: service_healthy environment: PG_META_PORT: 8080 @@ -317,23 +294,26 @@ services: PG_META_DB_NAME: ${POSTGRES_DB} PG_META_DB_USER: supabase_admin PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} + CRYPTO_KEY: ${PG_META_CRYPTO_KEY} functions: - container_name: ${CONTAINER_PREFIX}-edge-functions - image: supabase/edge-runtime:v1.67.4 + image: supabase/edge-runtime:v1.71.2 restart: unless-stopped volumes: - ../files/volumes/functions:/home/deno/functions:Z + - deno-cache:/root/.cache/deno depends_on: - analytics: + kong: condition: service_healthy environment: JWT_SECRET: ${JWT_SECRET} SUPABASE_URL: http://kong:8000 + SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL} SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY} + SUPABASE_PUBLISHABLE_KEYS: "{\"default\":\"${SUPABASE_PUBLISHABLE_KEY:-}\"}" + SUPABASE_SECRET_KEYS: "{\"default\":\"${SUPABASE_SECRET_KEY:-}\"}" SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} - # TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786 VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}" command: [ @@ -343,32 +323,23 @@ services: ] analytics: - container_name: ${CONTAINER_PREFIX}-analytics - image: supabase/logflare:1.12.0 + image: supabase/logflare:1.36.1 restart: unless-stopped - # ports: - # - 4000:4000 expose: - 4000 - # Uncomment to use Big Query backend for analytics - # volumes: - # - type: bind - # source: ${PWD}/gcloud.json - # target: /opt/app/rel/logflare/bin/gcloud.json - # read_only: true + # First boot: Logflare seeds _analytics in Postgres; allow extra time before marking unhealthy. healthcheck: test: [ - "CMD", - "curl", - "http://localhost:4000/health" + "CMD-SHELL", + "curl -sSfL -o /dev/null http://localhost:4000/health" ] - timeout: 5s + timeout: 10s interval: 5s - retries: 10 + retries: 15 + start_period: 90s depends_on: db: - # Disable this if you are using an external Postgres database condition: service_healthy environment: LOGFLARE_NODE_HOST: 127.0.0.1 @@ -378,55 +349,42 @@ services: DB_PORT: ${POSTGRES_PORT} DB_PASSWORD: ${POSTGRES_PASSWORD} DB_SCHEMA: _analytics - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} - LOGFLARE_SINGLE_TENANT: true - LOGFLARE_SUPABASE_MODE: true + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} + LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + LOGFLARE_SINGLE_TENANT: "true" + LOGFLARE_SUPABASE_MODE: "true" LOGFLARE_MIN_CLUSTER_SIZE: 1 - - # Comment variables to use Big Query backend for analytics POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase POSTGRES_BACKEND_SCHEMA: _analytics LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true - # Uncomment to use Big Query backend for analytics - # GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID} - # GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER} - # Comment out everything below this point if you are using an external Postgres database db: - container_name: ${CONTAINER_PREFIX}-db - image: supabase/postgres:15.8.1.060 + image: supabase/postgres:15.8.1.085 restart: unless-stopped volumes: - ../files/volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z - # Must be superuser to create event trigger - ../files/volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z - # Must be superuser to alter reserved role - ../files/volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z - # Initialize the database settings with JWT_SECRET and JWT_EXP - ../files/volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z - # PGDATA directory is persisted between restarts - ../files/volumes/db/data:/var/lib/postgresql/data:Z - # Changes required for internal supabase data such as _analytics - ../files/volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z - # Changes required for Analytics support - ../files/volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z - # Changes required for Pooler support - ../files/volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z - # Use named volume to persist pgsodium decryption key between restarts - db-config:/etc/postgresql-custom healthcheck: test: [ - "CMD", - "pg_isready", - "-U", - "postgres", - "-h", - "localhost" + "CMD", + "pg_isready", + "-U", + "postgres", + "-h", + "localhost" ] interval: 5s timeout: 5s - retries: 10 + retries: 15 + start_period: 60s depends_on: vector: condition: service_healthy @@ -446,12 +404,11 @@ services: "-c", "config_file=/etc/postgresql/postgresql.conf", "-c", - "log_min_messages=fatal" # prevents Realtime polling queries from appearing in logs + "log_min_messages=fatal" ] vector: - container_name: ${CONTAINER_PREFIX}-vector - image: timberio/vector:0.28.1-alpine + image: timberio/vector:0.53.0-alpine restart: unless-stopped volumes: - ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z @@ -468,9 +425,10 @@ services: ] timeout: 5s interval: 5s - retries: 3 + retries: 5 + start_period: 10s environment: - LOGFLARE_API_KEY: ${LOGFLARE_API_KEY} + LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} command: [ "--config", @@ -479,14 +437,12 @@ services: security_opt: - "label=disable" - # Update the DATABASE_URL if you are using an external Postgres database supavisor: - container_name: ${CONTAINER_PREFIX}-pooler - image: supabase/supavisor:2.5.1 + image: supabase/supavisor:2.7.4 restart: unless-stopped - ports: # expose supavisor to the host to enable db pooler connection - - ${POSTGRES_PORT}:5432 - - ${POOLER_PROXY_PORT_TRANSACTION}:6543 + expose: + - "5432" + - "6543" volumes: - ../files/volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro,z healthcheck: @@ -502,19 +458,19 @@ services: ] interval: 10s timeout: 5s - retries: 5 + retries: 10 + start_period: 30s depends_on: db: condition: service_healthy - analytics: - condition: service_healthy environment: PORT: 4000 POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase - CLUSTER_POSTGRES: true + DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase + CLUSTER_POSTGRES: "true" SECRET_KEY_BASE: ${SECRET_KEY_BASE} VAULT_ENC_KEY: ${VAULT_ENC_KEY} API_JWT_SECRET: ${JWT_SECRET} @@ -525,6 +481,7 @@ services: POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE} POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN} POOLER_POOL_MODE: transaction + DB_POOL_SIZE: ${POOLER_DB_POOL_SIZE} command: [ "/bin/sh", @@ -533,4 +490,5 @@ services: ] volumes: - db-config: \ No newline at end of file + db-config: + deno-cache: diff --git a/blueprints/supabase/template.toml b/blueprints/supabase/template.toml index f70c93cc..0e76b7dd 100644 --- a/blueprints/supabase/template.toml +++ b/blueprints/supabase/template.toml @@ -2,11 +2,15 @@ main_domain = "${domain}" postgres_password = "${password:32}" dashboard_password = "${password:32}" -logflare_api_key = "${password:32}" +logflare_public_access_token = "${password:32}" +logflare_private_access_token = "${password:32}" +pg_meta_crypto_key = "${password:32}" +s3_protocol_access_key_id = "${password:24}" +s3_protocol_access_key_secret = "${password:48}" secret_key_base = "${password:64}" vault_enc_key = "${password:32}" jwt_secret = "${password:32}" -container_name_prefix = "${APP_NAME}-supabase" +pooler_tenant_id = "${uuid}" anon_key_payload = """{ "role": "anon", "iss": "supabase", @@ -34,12 +38,7 @@ env = [ '# - ADDITIONAL_REDIRECT_URLS, SITE_URL should point to application using supabase for authentication', '# They are used for redirecting after login/signup and gotrue will check them before sending emails', '# - POSTGRES_PORT, POOLER_PROXY_PORT_TRANSACTION should be changed if you are already running other instances of supabase', -'#', -'# Supabase uses container names in part of its configuration so it is important to keep them', -'# This template generates a random prefix for the container names to avoid conflicts', -'# If you change it you will need to update routes in the vector.yml file in advanced->mounts section', '############', -'CONTAINER_PREFIX=${container_name_prefix}', '', '############', '# Secrets', @@ -59,6 +58,9 @@ env = [ 'DASHBOARD_PASSWORD=${dashboard_password}', 'SECRET_KEY_BASE=${secret_key_base}', 'VAULT_ENC_KEY=${vault_enc_key}', +'PG_META_CRYPTO_KEY=${pg_meta_crypto_key}', +'LOGFLARE_PUBLIC_ACCESS_TOKEN=${logflare_public_access_token}', +'LOGFLARE_PRIVATE_ACCESS_TOKEN=${logflare_private_access_token}', '', '', '############', @@ -68,7 +70,7 @@ env = [ 'POSTGRES_HOST=db', 'POSTGRES_DB=postgres', 'POSTGRES_PORT=5432', -'# default user is postgres', +'POSTGRES_USER=postgres', '', '', '############', @@ -77,7 +79,8 @@ env = [ 'POOLER_PROXY_PORT_TRANSACTION=6543', 'POOLER_DEFAULT_POOL_SIZE=20', 'POOLER_MAX_CLIENT_CONN=100', -'POOLER_TENANT_ID=your-tenant-id', +'POOLER_TENANT_ID=${pooler_tenant_id}', +'POOLER_DB_POOL_SIZE=5', '', '', '############', @@ -94,6 +97,8 @@ env = [ '############', '', 'PGRST_DB_SCHEMAS=public,storage,graphql_public', +'PGRST_DB_MAX_ROWS=1000', +'PGRST_DB_EXTRA_SEARCH_PATH=public', '', '', '############', @@ -102,10 +107,11 @@ env = [ '', '## General', 'SITE_URL=http://localhost:3000', -'ADDITIONAL_REDIRECT_URLS=http://${main_domain}/*,http://localhost:3000/*', +'ADDITIONAL_REDIRECT_URLS=https://${main_domain}/*,http://localhost:3000/*', 'JWT_EXPIRY=3600', 'DISABLE_SIGNUP=false', -'API_EXTERNAL_URL=http://${main_domain}', +'API_EXTERNAL_URL=https://${main_domain}', +'JWT_KEYS=[]', '', '## Mailer Config', 'MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"', @@ -136,12 +142,19 @@ env = [ 'STUDIO_DEFAULT_ORGANIZATION=Default Organization', 'STUDIO_DEFAULT_PROJECT=Default Project', '', -'STUDIO_PORT=3000', -'# replace if you intend to use Studio outside of localhost', -'SUPABASE_PUBLIC_URL=http://${main_domain}', +'SUPABASE_PUBLIC_URL=https://${main_domain}', '', -'# Enable webp support', -'IMGPROXY_ENABLE_WEBP_DETECTION=true', +'############', +'# Storage - local file backend (default). Edit docker-compose.yml storage env for S3/MinIO.', +'############', +'', +'GLOBAL_S3_BUCKET=stub', +'REGION=stub', +'STORAGE_TENANT_ID=stub', +'S3_PROTOCOL_ACCESS_KEY_ID=${s3_protocol_access_key_id}', +'S3_PROTOCOL_ACCESS_KEY_SECRET=${s3_protocol_access_key_secret}', +'', +'IMGPROXY_AUTO_WEBP=true', '', '# Add your OpenAI API key to enable SQL Editor Assistant', 'OPENAI_API_KEY=', @@ -156,14 +169,8 @@ env = [ '', '############', '# Logs - Configuration for Logflare', -'# Please refer to https://supabase.com/docs/reference/self-hosting-analytics/introduction', '############', '', -'LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key', -'', -'# Change vector.toml sinks to reflect this change', -'LOGFLARE_API_KEY=${logflare_api_key}', -'', '# Docker socket location - this value will differ depending on your OS', 'DOCKER_SOCKET_LOCATION=/var/run/docker.sock', '', @@ -171,6 +178,40 @@ env = [ 'GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID', 'GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER'] +[[config.mounts]] +filePath = "/volumes/api/kong-entrypoint.sh" +content = """#!/bin/bash +# Custom entrypoint for Kong that builds Lua expressions for request-transformer +# and performs environment variable substitution in the declarative config. + +if [ -n "$SUPABASE_SECRET_KEY" ] && [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then + export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)" + export LUA_RT_WS_EXPR="\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)" +else + export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)" + export LUA_RT_WS_EXPR="\$(query_params.apikey)" +fi + +awk '{ + result = "" + rest = $0 + while (match(rest, /\$[A-Za-z_][A-Za-z_0-9]*/)) { + varname = substr(rest, RSTART + 1, RLENGTH - 1) + if (varname in ENVIRON) { + result = result substr(rest, 1, RSTART - 1) ENVIRON[varname] + } else { + result = result substr(rest, 1, RSTART + RLENGTH - 1) + } + rest = substr(rest, RSTART + RLENGTH) + } + print result rest +}' /home/kong/temp.yml > "$KONG_DECLARATIVE_CONFIG" + +sed -i '/^[[:space:]]*- key:[[:space:]]*$/d' "$KONG_DECLARATIVE_CONFIG" + +exec /entrypoint.sh kong docker-start +""" + [[config.mounts]] filePath = "/volumes/api/kong.yml" content = """_format_version: '2.1' @@ -184,9 +225,11 @@ consumers: - username: anon keyauth_credentials: - key: $SUPABASE_ANON_KEY + - key: $SUPABASE_PUBLISHABLE_KEY - username: service_role keyauth_credentials: - key: $SUPABASE_SERVICE_KEY + - key: $SUPABASE_SECRET_KEY ### ### Access Control List @@ -202,8 +245,8 @@ acls: ### basicauth_credentials: - consumer: DASHBOARD - username: $DASHBOARD_USERNAME - password: $DASHBOARD_PASSWORD + username: '$DASHBOARD_USERNAME' + password: '$DASHBOARD_PASSWORD' ### ### API Routes @@ -237,6 +280,33 @@ services: - /auth/v1/authorize plugins: - name: cors + - name: auth-v1-open-jwks + url: http://auth:9999/.well-known/jwks.json + routes: + - name: auth-v1-open-jwks + strip_path: true + paths: + - /auth/v1/.well-known/jwks.json + plugins: + - name: cors + - name: auth-v1-open-sso-acs + url: http://auth:9999/sso/saml/acs + routes: + - name: auth-v1-open-sso-acs + strip_path: true + paths: + - /sso/saml/acs + plugins: + - name: cors + - name: auth-v1-open-sso-metadata + url: http://auth:9999/sso/saml/metadata + routes: + - name: auth-v1-open-sso-metadata + strip_path: true + paths: + - /sso/saml/metadata + plugins: + - name: cors ## Secure Auth routes - name: auth-v1 @@ -252,6 +322,14 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -272,7 +350,15 @@ services: - name: cors - name: key-auth config: - hide_credentials: true + hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -298,7 +384,11 @@ services: config: add: headers: - - Content-Profile:graphql_public + - "Content-Profile: graphql_public" + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true @@ -309,7 +399,7 @@ services: ## Secure Realtime routes - name: realtime-v1-ws _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' - url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/socket + url: http://realtime:4000/socket protocol: ws routes: - name: realtime-v1-ws @@ -321,6 +411,14 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "x-api-key:$LUA_RT_WS_EXPR" + replace: + querystring: + - "apikey:$LUA_RT_WS_EXPR" - name: acl config: hide_groups_header: true @@ -328,8 +426,8 @@ services: - admin - anon - name: realtime-v1-rest - _comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*' - url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/api + _comment: 'Realtime: /realtime/v1/api/* -> http://realtime:4000/api/*' + url: http://realtime:4000/api protocol: http routes: - name: realtime-v1-rest @@ -341,13 +439,22 @@ services: - name: key-auth config: hide_credentials: false + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" - name: acl config: hide_groups_header: true allow: - admin - anon - ## Storage routes: the storage server manages its own auth + + ## Storage routes - name: storage-v1 _comment: 'Storage: /storage/v1/* -> http://storage:5000/*' url: http://storage:5000/ @@ -358,11 +465,28 @@ services: - /storage/v1/ plugins: - name: cors + - name: request-transformer + config: + add: + headers: + - "Authorization: $LUA_AUTH_EXPR" + replace: + headers: + - "Authorization: $LUA_AUTH_EXPR" + - name: post-function + config: + access: + - | + local auth = kong.request.get_header("authorization") + if auth == nil or auth == "" or auth:find("^%s*$") then + kong.service.request.clear_header("authorization") + end ## Edge Functions routes - name: functions-v1 _comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*' url: http://functions:9000/ + read_timeout: 150000 routes: - name: functions-v1-all strip_path: true @@ -371,15 +495,16 @@ services: plugins: - name: cors - ## Analytics routes - - name: analytics-v1 - _comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*' - url: http://analytics:4000/ + ## OAuth 2.0 Authorization Server Metadata + - name: well-known-oauth + url: http://auth:9999/.well-known/oauth-authorization-server routes: - - name: analytics-v1-all + - name: well-known-oauth strip_path: true paths: - - /analytics/v1/ + - /.well-known/oauth-authorization-server + plugins: + - name: cors ## Secure Database routes - name: meta @@ -400,6 +525,32 @@ services: allow: - admin + - name: mcp-blocker + url: http://studio:3000/api/mcp + routes: + - name: mcp-blocker-route + strip_path: true + paths: + - /api/mcp + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + + - name: mcp + url: http://studio:3000/api/mcp + routes: + - name: mcp + strip_path: true + paths: + - /mcp + plugins: + - name: request-termination + config: + status_code: 403 + message: "Access is forbidden." + ## Protected Dashboard - catch all remaining routes - name: dashboard _comment: 'Studio: /* -> http://studio:3000/*' @@ -416,6 +567,10 @@ services: hide_credentials: true """ +[[config.mounts]] +filePath = "/volumes/snippets/.gitkeep" +content = "" + [[config.mounts]] filePath = "/volumes/db/init/data.sql" content = "" @@ -717,14 +872,25 @@ serve(async () => { [[config.mounts]] filePath = "/volumes/functions/main/index.ts" -content = """import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' -import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' +content = """import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' console.log('main function started') const JWT_SECRET = Deno.env.get('JWT_SECRET') +const SUPABASE_URL = Deno.env.get('SUPABASE_URL') const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' +let SUPABASE_JWT_KEYS: ReturnType | null = null +if (SUPABASE_URL) { + try { + SUPABASE_JWT_KEYS = jose.createRemoteJWKSet( + new URL('/auth/v1/.well-known/jwks.json', SUPABASE_URL) + ) + } catch (e) { + console.error('Failed to fetch JWKS from SUPABASE_URL:', e) + } +} + function getAuthToken(req: Request) { const authHeader = req.headers.get('authorization') if (!authHeader) { @@ -737,25 +903,53 @@ function getAuthToken(req: Request) { return token } -async function verifyJWT(jwt: string): Promise { +async function isValidLegacyJWT(jwt: string): Promise { + if (!JWT_SECRET) { + console.error('JWT_SECRET not available for HS256 token verification') + return false + } const encoder = new TextEncoder() const secretKey = encoder.encode(JWT_SECRET) try { await jose.jwtVerify(jwt, secretKey) - } catch (err) { - console.error(err) + } catch (e) { + console.error('Symmetric Legacy JWT verification error', e) return false } return true } -serve(async (req: Request) => { +async function isValidJWT(jwt: string): Promise { + if (!SUPABASE_JWT_KEYS) { + console.error('JWKS not available for ES256/RS256 token verification') + return false + } + try { + await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS) + } catch (e) { + console.error('Asymmetric JWT verification error', e) + return false + } + return true +} + +async function isValidHybridJWT(jwt: string): Promise { + const { alg: jwtAlgorithm } = jose.decodeProtectedHeader(jwt) + if (jwtAlgorithm === 'HS256') { + return await isValidLegacyJWT(jwt) + } + if (jwtAlgorithm === 'ES256' || jwtAlgorithm === 'RS256') { + return await isValidJWT(jwt) + } + return false +} + +Deno.serve(async (req: Request) => { if (req.method !== 'OPTIONS' && VERIFY_JWT) { try { const token = getAuthToken(req) - const isValidJWT = await verifyJWT(token) - - if (!isValidJWT) { + const isValid = await isValidHybridJWT(token) + if (!isValid) { return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { status: 401, headers: { 'Content-Type': 'application/json' }, @@ -822,8 +1016,6 @@ content = """api: sources: docker_host: type: docker_logs - exclude_containers: - - ${container_name_prefix}-vector transforms: project_logs: @@ -833,7 +1025,15 @@ transforms: source: |- .project = "default" .event_message = del(.message) - .appname = replace!(del(.container_name), "${container_name_prefix}", "supabase") + compose_service, label_err = get(.label, ["com.docker.compose.service"]) + if label_err != null || compose_service == null { + abort + } + compose_service = to_string!(compose_service) + if compose_service == "vector" { + abort + } + .appname = "supabase-" + compose_service del(.container_created_at) del(.container_id) del(.source_type) @@ -847,12 +1047,12 @@ transforms: inputs: - project_logs route: - kong: '.appname == "supabase-kong"' + kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"' auth: '.appname == "supabase-auth"' rest: '.appname == "supabase-rest"' - realtime: '.appname == "realtime-dev.${CONTAINER_PREFIX}-realtime"' + realtime: '.appname == "supabase-realtime"' storage: '.appname == "supabase-storage"' - functions: '.appname == "supabase-edge-functions"' + functions: '.appname == "supabase-functions"' db: '.appname == "supabase-db"' # Ignores non nginx errors since they are related with kong booting up kong_logs: @@ -866,15 +1066,17 @@ transforms: .metadata.request.headers.referer = req.referer .metadata.request.headers.user_agent = req.agent .metadata.request.headers.cf_connecting_ip = req.client - .metadata.request.method = req.method - .metadata.request.path = req.path - .metadata.request.protocol = req.protocol .metadata.response.status_code = req.status + url, split_err = split(req.request, " ") + if split_err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } } if err != null { abort } - # Ignores non nginx errors since they are related with kong booting up kong_err: type: remap inputs: @@ -918,14 +1120,18 @@ transforms: parsed, err = parse_regex(.event_message, r'^(?P