Compare commits

..
Author SHA1 Message Date
Mauricio SiuandClaude Fable 5 21c1fec5d8 feat: validator opt-out marker for intentional host ports + annotate rustdesk
The validate-docker-compose CI on this PR was failing on rustdesk's
hbbs/hbbr port mappings (21115-21119) — intentional host-published ports
for the RustDesk relay/rendezvous protocol, which Traefik cannot route.

Adds a '# dokploy: allow-host-ports' top-of-file marker that downgrades
the port-mapping errors to warnings for such protocol templates
(mail/game/streaming/VPN/remote-desktop), and annotates rustdesk.
Other exception templates (poste.io, mailu, oryx, fivem...) can be
annotated case-by-case as the weekly audit (#1049) surfaces them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 01:22:34 -06:00
Mauricio Siu f8a75dba2b Merge remote-tracking branch 'origin/canary' into fix/normalize-empty-mounts 2026-08-06 01:21:15 -06:00
Mauricio SiuandClaude Fable 5 b9c4365e9f fix: normalize template.toml mounts across all blueprints (closes #1047)
- 79 blueprints declared a bare [[config.mounts]] header (one empty array
  element in TOML) -> replaced with the repo idiom 'mounts = []'
- 38 blueprints carried doc-style mount blocks (name/mountPath/description)
  that don't match the schema (filePath/content) and are inert in Dokploy's
  template processor -> removed; their named volumes already live in each
  docker-compose.yml
- penpot: duplicate 'expose' map key made its docker-compose.yml invalid
  YAML -> merged into one expose list

After this, validate-template.ts passes for all 502 blueprints. The
remaining validate-docker-compose.ts flags are the intentional host-port
templates (mail/game/streaming protocols: poste.io, mailu, oryx, fivem,
lodestone, etc.) which the validator does not yet model as exceptions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 04:28:49 -06:00
98 changed files with 113 additions and 627 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

-32
View File
@@ -1,32 +0,0 @@
version: "3.8"
services:
9router:
image: decolua/9router:0.5.40
restart: unless-stopped
environment:
DATA_DIR: /app/data
PORT: "20128"
HOSTNAME: "0.0.0.0"
NODE_ENV: production
JWT_SECRET: ${JWT_SECRET}
INITIAL_PASSWORD: ${INITIAL_PASSWORD}
API_KEY_SECRET: ${API_KEY_SECRET}
MACHINE_ID_SALT: ${MACHINE_ID_SALT}
BASE_URL: http://${ROUTER_HOST}
NEXT_PUBLIC_BASE_URL: http://${ROUTER_HOST}
HEADROOM_URL: http://headroom:8787
AUTH_COOKIE_SECURE: "false"
volumes:
- 9router-data:/app/data
depends_on:
- headroom
# Optional token-reduction proxy that 9Router calls through HEADROOM_URL.
# Shipped as a sidecar because the 9Router image does not bundle it.
headroom:
image: ghcr.io/chopratejas/headroom:0.6.7
restart: unless-stopped
volumes:
9router-data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "9router",
"name": "9Router",
"version": "0.5.40",
"description": "Self-hosted AI model router that rotates requests across 40+ providers with automatic fallback, exposing an OpenAI-compatible endpoint for coding agents.",
"logo": "9router.png",
"links": {
"github": "https://github.com/decolua/9router",
"website": "https://9router.com",
"docs": "https://github.com/decolua/9router/blob/master/DOCKER.md"
},
"tags": [
"ai",
"proxy",
"developer-tools"
]
}
-21
View File
@@ -1,21 +0,0 @@
[variables]
main_domain = "${domain}"
jwt_secret = "${password:64}"
initial_password = "${password:32}"
api_key_secret = "${password:64}"
machine_id_salt = "${password:32}"
[config]
env = [
"ROUTER_HOST=${main_domain}",
"JWT_SECRET=${jwt_secret}",
"INITIAL_PASSWORD=${initial_password}",
"API_KEY_SECRET=${api_key_secret}",
"MACHINE_ID_SALT=${machine_id_salt}",
]
mounts = []
[[config.domains]]
serviceName = "9router"
port = 20128
host = "${main_domain}"
+1 -2
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
timezone = "America/New_York"
[config]
mounts = []
[[config.domains]]
serviceName = "agentdvr"
port = 8090
@@ -10,5 +11,3 @@ host = "${main_domain}"
[config.env]
TIMEZONE = "${timezone}"
[[config.mounts]]
+2 -2
View File
@@ -24,6 +24,7 @@ port = 80
host = "${main_domain}"
[config]
mounts = []
env = [
# App
"APP_URL=${main_domain}",
@@ -50,5 +51,4 @@ env = [
"MYSQL_RANDOM_ROOT_PASSWORD=yes"
]
# No custom mounts needed; volumes are already defined in compose.
[[config.mounts]]
# No custom mounts needed; volumes are already defined in compose.
+1 -2
View File
@@ -4,6 +4,7 @@ mysql_root_password = "${password:32}"
mysql_password = "${password:16}"
[config]
mounts = []
[[config.domains]]
serviceName = "azuracast"
port = 80
@@ -27,5 +28,3 @@ NGINX_RADIO_PORTS = "8000,8010,8020,8030,8040,8050"
PREFER_RELEASE_BUILDS = "true"
COMPOSER_PLUGIN_MODE = "false"
ADDITIONAL_MEDIA_SYNC_WORKER_COUNT = "0"
[[config.mounts]]
+1 -4
View File
@@ -72,7 +72,7 @@ s3_endpoint = ""
s3_bucket = ""
[config]
mounts = []
[[config.domains]]
serviceName = "webapp"
port = 80
@@ -165,6 +165,3 @@ MYSQL_DATABASE = "${system_db_name}"
MYSQL_USER = "${db_user}"
MYSQL_PASSWORD = "${db_password}"
MYSQL_ROOT_PASSWORD = "${db_root_password}"
[[config.mounts]]
+1 -2
View File
@@ -4,6 +4,7 @@ app_password = "${password:32}"
db_root_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "booklore"
port = 6060
@@ -20,5 +21,3 @@ MYSQL_USER = "booklore"
MYSQL_PASSWORD = "${app_password}"
# API Key
MYSQL_ROOT_PASSWORD = "${db_root_password}"
[[config.mounts]]
+1 -15
View File
@@ -2,24 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "borgitory"
port = 8000
host = "${main_domain}"
[config.env]
[[config.mounts]]
name = "borgitory-data"
mountPath = "/app/data"
description = "Database and encryption key storage"
[[config.mounts]]
name = "borgitory-sources"
mountPath = "/mnt/sources"
description = "Sources to back up (read-only)"
[[config.mounts]]
name = "borgitory-repos"
mountPath = "/mnt/repos"
description = "Borg repositories (read-only)"
+1 -7
View File
@@ -3,18 +3,12 @@ main_domain = "${domain}"
postgres_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "budget-board-client"
port = 6253
host = "${main_domain}"
path = "/"
[config.env]
POSTGRES_PASSWORD = "${postgres_password}"
[[config.mounts]]
source = "../files/db-data"
target = "/var/lib/postgresql/data"
type = "bind"
+1 -1
View File
@@ -2,7 +2,7 @@ version: "3.8"
services:
chatto:
image: ghcr.io/chattocorp/chatto:0.4
image: ghcr.io/chattocorp/chatto:0.3.8
restart: unless-stopped
expose:
- "4000"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "chatto",
"name": "Chatto (single binary)",
"version": "0.4.x",
"version": "0.3.8",
"description": "A fully-featured real-time chat application for teams and communities. This template deploys the single Chatto binary with embedded NATS. Note: email/password registration requires SMTP to be configured after deployment; voice/video calls are not included in the single-binary setup.",
"logo": "logo.png",
"links": {
+1 -4
View File
@@ -2,13 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "checkcle"
port = 8090
host = "${main_domain}"
[config.env]
[[config.mounts]]
source = "checkcle-data"
target = "/mnt/pb_data"
+1 -5
View File
@@ -11,6 +11,7 @@ hide_history = "false"
language = "en"
[config]
mounts = []
[[config.domains]]
serviceName = "convertx"
port = 3000
@@ -26,8 +27,3 @@ WEBROOT = "${webroot}"
FFMPEG_ARGS = "${ffmpeg_args}"
HIDE_HISTORY = "${hide_history}"
LANGUAGE = "${language}"
[[config.mounts]]
source = "../files/data"
target = "/app/data"
type = "bind"
+1 -3
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "cookiecloud"
port = 8088
@@ -9,6 +10,3 @@ host = "${main_domain}"
[config.env]
# No environment variables required by default
[[config.mounts]]
# Data volume mount is defined in docker-compose.yml
+1 -7
View File
@@ -2,16 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "cup"
port = 8000
host = "${main_domain}"
[config.env]
[[config.mounts]]
serviceName = "cup"
source = "/var/run/docker.sock"
target = "/var/run/docker.sock"
type = "bind"
+1 -2
View File
@@ -2,6 +2,7 @@
app_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "directory-lister" # Must match the service name in docker-compose.yml
port = 80
@@ -15,5 +16,3 @@ READMES_FIRST = "false"
ZIP_DOWNLOADS = "true"
TIMEZONE = "UTC"
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "docling-serve"
port = 5001
@@ -11,5 +12,3 @@ host = "${main_domain}"
DOCLING_SERVE_ENABLE_UI = "1"
DOCLING_SERVE_HOST = "0.0.0.0"
DOCLING_SERVE_PORT = "5001"
[[config.mounts]]
+1 -5
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
pg_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "app"
port = 3000
@@ -26,8 +27,3 @@ DL_PG_NAME = "domain_locker"
# DL_DOMAIN_INFO_API = ""
# DL_DOMAIN_SUBS_API = ""
# DL_DISABLE_WRITE_METHODS = "false"
[[config.mounts]]
# Example mount for PostgreSQL persistence (already defined in compose volumes)
# filePath = "/var/lib/postgresql/data"
# content = ""
+1 -5
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
docker_socket = "/var/run/docker.sock"
[config]
mounts = []
[[config.domains]]
serviceName = "dozzle"
port = 8080
@@ -11,8 +12,3 @@ host = "${main_domain}"
[config.env]
DOZZLE_USERNAME = "${username}"
DOZZLE_PASSWORD = "${password:16}"
[[config.mounts]]
source = "${docker_socket}"
target = "/var/run/docker.sock"
read_only = true
+1 -6
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
default_pin = "${password:4}"
[config]
mounts = []
[[config.domains]]
serviceName = "dumbassets"
port = 3000
@@ -19,9 +20,3 @@ DEMO_MODE = "false"
APPRISE_URL = ""
CURRENCY_CODE = "USD"
CURRENCY_LOCALE = "en-US"
[[config.mounts]]
serviceName = "dumbassets"
type = "volume"
source = "dumbassets-data"
target = "/app/data"
+2 -2
View File
@@ -4,6 +4,7 @@ main_domain = "${domain}"
dumbbudget_pin = "${password:16}"
[config]
mounts = []
[[config.domains]]
serviceName = "dumbbudget"
port = 3000
@@ -20,5 +21,4 @@ ALLOWED_ORIGINS = "${main_domain}"
# The named volume 'dumbbudget-data' is defined in the docker-compose.yml.
# According to Dokploy's template examples, volumes declared in the compose
# file are automatically managed and do not require a separate entry here.
[[config.mounts]]
# file are automatically managed and do not require a separate entry here.
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "https://${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "dumbdrop"
port = 3000
@@ -20,5 +21,3 @@ APPRISE_URL = ""
APPRISE_MESSAGE = "New file uploaded {filename} ({size}), Storage used {storage}"
APPRISE_SIZE_UNIT = "Auto"
ALLOWED_EXTENSIONS = ""
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "dumbpad"
port = 3000
@@ -17,5 +18,3 @@ LOCKOUT_TIME = "15"
MAX_ATTEMPTS = "5"
COOKIE_MAX_AGE = "24"
PAGE_HISTORY_COOKIE_AGE = "365"
[[config.mounts]]
+1 -2
View File
@@ -7,6 +7,7 @@ root_pass = "${password:32}"
secret_key = "${password:64}"
[config]
mounts = []
[[config.domains]]
serviceName = "ezbookkeeping"
port = 8080
@@ -27,5 +28,3 @@ EBK_DATABASE_PASSWD = "${db_pass}"
EBK_LOG_MODE = "file"
# Security secret key used for application protection
EBK_SECURITY_SECRET_KEY = "${secret_key}"
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "flaresolverr"
port = 8191
@@ -12,5 +13,3 @@ LOG_LEVEL = "info"
LOG_HTML = "false"
CAPTCHA_SOLVER = "none"
TZ = "Europe/London"
[[config.mounts]]
+1 -4
View File
@@ -6,6 +6,7 @@ secret_key = "${password:32}"
totp_key = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "flatnotes"
port = 8080
@@ -22,7 +23,3 @@ FLATNOTES_SECRET_KEY = "${secret_key}"
FLATNOTES_TOTP_KEY = "${totp_key}"
FLATNOTES_SESSION_EXPIRY_DAYS = "30"
FLATNOTES_PATH_PREFIX = ""
[[config.mounts]]
name = "flatnotes-data"
mountPath = "/data"
+1 -4
View File
@@ -5,6 +5,7 @@ password = "${password:16}"
secret_key = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "flatnotes"
port = 8080
@@ -26,7 +27,3 @@ FLATNOTES_QUICK_ACCESS_TITLE = "RECENTLY MODIFIED"
FLATNOTES_QUICK_ACCESS_TERM = "*"
FLATNOTES_QUICK_ACCESS_SORT = "lastModified"
FLATNOTES_QUICK_ACCESS_LIMIT = "4"
[[config.mounts]]
name = "flatnotes-data"
mountPath = "/data"
+1 -8
View File
@@ -5,6 +5,7 @@ jwt_refresh_secret = "${password:32}"
express_secret = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "flowise"
port = 3000
@@ -14,11 +15,3 @@ host = "${main_domain}"
JWT_AUTH_TOKEN_SECRET = "${jwt_secret}"
JWT_REFRESH_TOKEN_SECRET = "${jwt_refresh_secret}"
EXPRESS_SESSION_SECRET = "${express_secret}"
[[config.mounts]]
name = "flowise_data"
mountPath = "/root/.flowise"
[[config.mounts]]
name = "redis_data"
mountPath = "/data"
+1 -1
View File
@@ -1,6 +1,6 @@
services:
fmd-server:
image: registry.gitlab.com/fmd-foss/fmd-server:0.16.0
image: registry.gitlab.com/fmd-foss/fmd-server:v0.11.0
expose:
- 8080
environment:
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "fmd-server",
"name": "FMD Server",
"version": "0.16.0",
"version": "0.11.0",
"description": "A server to communicate with the FMD Android app, to locate and control your devices.",
"logo": "fmd-server.svg",
"links": {
+1 -2
View File
@@ -4,6 +4,7 @@ db_password = "${password:24}"
db_root_password = "${password:24}"
[config]
mounts = []
[[config.domains]]
serviceName = "gitea"
port = 3000
@@ -19,5 +20,3 @@ GITEA__database__USER = "gitea"
GITEA__database__PASSWD = "${db_password}"
GITEA_DB_PASSWORD = "${db_password}"
MYSQL_ROOT_PASSWORD = "${db_root_password}"
[[config.mounts]]
+1 -2
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
db_password = "${password:24}"
[config]
mounts = []
[[config.domains]]
serviceName = "gitea"
port = 3000
@@ -17,5 +18,3 @@ GITEA__database__NAME = "gitea"
GITEA__database__USER = "gitea"
GITEA__database__PASSWD = "${db_password}"
GITEA_DB_PASSWORD = "${db_password}"
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "gitea"
port = 3000
@@ -19,5 +20,3 @@ USER_GID = "1000"
# GITEA__mailer__SMTP_PORT = "465"
# GITEA__mailer__USER = "apikey"
# GITEA__mailer__PASSWD = "${password:32}"
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "grimoire"
port = 5173
@@ -12,5 +13,3 @@ PORT = "5173"
PUBLIC_ORIGIN = "https://${main_domain}"
PUBLIC_HTTPS_ONLY = "true"
PUBLIC_SIGNUP_DISABLED = "false"
[[config.mounts]]
+1 -3
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
default_email = "${email}"
[config]
mounts = []
[[config.domains]]
serviceName = "grist"
port = 8484
@@ -10,6 +11,3 @@ host = "${main_domain}"
[config.env]
GRIST_DEFAULT_EMAIL = "${default_email}"
[[config.mounts]]
+1 -6
View File
@@ -6,6 +6,7 @@ mongo_admin_password = "${password}"
mongo_habitica_password = "${password}"
[config]
mounts = []
[[config.domains]]
serviceName = "client"
port = 80
@@ -24,9 +25,3 @@ MONGO_ADMIN_PASSWORD = "${mongo_admin_password}"
MONGO_HABITICA_USER = "habitica"
MONGO_HABITICA_PASSWORD = "${mongo_habitica_password}"
ADMIN_EMAIL = "no-reply@${main_domain}"
[[config.mounts]]
serviceName = "mongo"
type = "volume"
source = "habitica-mongo-data"
target = "/data/db"
+1 -3
View File
@@ -4,7 +4,7 @@ db_password = "${password:32}"
encryption_key = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "hoppscotch"
port = 80
@@ -37,5 +37,3 @@ VITE_APP_PRIVACY_POLICY_LINK = "https://docs.hoppscotch.io/support/privacy"
# Subpath Access
ENABLE_SUBPATH_BASED_ACCESS = "true"
[[config.mounts]]
+1 -25
View File
@@ -10,7 +10,7 @@ DB_PASSWORD = "${password:20}"
MARIADB_ROOT_PASSWORD = "${password:24}"
[config]
mounts = []
[[config.domains]]
serviceName = "app"
port = 80
@@ -24,27 +24,3 @@ DB_DATABASE = "${DB_DATABASE}"
DB_USERNAME = "${DB_USERNAME}"
DB_PASSWORD = "${DB_PASSWORD}"
MARIADB_ROOT_PASSWORD = "${MARIADB_ROOT_PASSWORD}"
[[config.mounts]]
name = "app_images"
mountPath = "/var/www/html/public/img"
[[config.mounts]]
name = "app_logs"
mountPath = "/var/www/html/app/logs"
[[config.mounts]]
name = "app_backup"
mountPath = "/var/www/html/public/backup"
[[config.mounts]]
name = "app_themes"
mountPath = "/var/www/html/public/themes"
[[config.mounts]]
name = "app_migrate"
mountPath = "/var/www/html/app/migrations"
[[config.mounts]]
name = "db_data"
mountPath = "/var/lib/mysql"
+1 -5
View File
@@ -4,7 +4,7 @@ jwt_secret = "${password:32}" # secure default
recovery_mode = "false"
[config]
mounts = []
[[config.domains]]
serviceName = "kuno"
port = 80
@@ -18,7 +18,3 @@ GIN_MODE = "release"
NODE_ENV = "production"
JWT_SECRET = "${jwt_secret}"
RECOVERY_MODE = "${recovery_mode}"
[[config.mounts]]
name = "kuno-data"
mountPath = "/app/data"
+1 -4
View File
@@ -6,6 +6,7 @@ MAIL_PASSWORD = "${password:32}"
SECRET_KEY = "${password:64}"
[config]
mounts = []
[[config.domains]]
serviceName = "ihatemoney"
port = 8000
@@ -37,7 +38,3 @@ LEGAL_LINK = ""
PORT = "8000"
PUID = "0"
PGID = "0"
[[config.mounts]]
volumeName = "sqlite-db"
mountPath = "/database"
+1 -10
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
karakeep_version = "release"
[config]
mounts = []
[[config.domains]]
serviceName = "web"
port = 3000
@@ -106,13 +107,3 @@ host = "${main_domain}"
# "CRAWLER_HTTP_PROXY" = ""
# "CRAWLER_HTTPS_PROXY" = ""
# "CRAWLER_NO_PROXY" = ""
[[config.mounts]]
# Persistent data directory for Karakeep
volumeName = "data"
mountPath = "/data"
[[config.mounts]]
# Meilisearch data directory
volumeName = "meilisearch"
mountPath = "/meili_data"
+1 -15
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
KENER_SECRET_KEY = "${password:64}"
[config]
mounts = []
[[config.domains]]
serviceName = "kener"
port = 3000
@@ -16,18 +17,3 @@ REDIS_URL = "redis://redis:6379"
DATABASE_URL = "sqlite://./database/kener.sqlite.db"
RESEND_API_KEY = ""
RESEND_SENDER_EMAIL = ""
[[config.mounts]]
type = "volume"
source = "data"
target = "/app/database"
[[config.mounts]]
type = "volume"
source = "redis_data"
target = "/data"
[[config.mounts]]
type = "bind"
source = "../files/uploads"
target = "/app/uploads"
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "kestra"
port = 8080
@@ -9,5 +10,3 @@ host = "${main_domain}"
[config.env]
[[config.mounts]]
+1 -2
View File
@@ -8,6 +8,7 @@ KEYCLOAK_ADMIN_PASSWORD = "${password:32}"
KC_HOSTNAME = "${main_domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "keycloak"
port = 8080
@@ -20,5 +21,3 @@ POSTGRES_PASSWORD = "${POSTGRES_PASSWORD}"
KEYCLOAK_ADMIN = "${KEYCLOAK_ADMIN}"
KEYCLOAK_ADMIN_PASSWORD = "${KEYCLOAK_ADMIN_PASSWORD}"
KC_HOSTNAME = "${KC_HOSTNAME}"
[[config.mounts]]
+2 -12
View File
@@ -6,7 +6,7 @@ db_user = "kitchenowl"
db_name = "kitchenowl"
[config]
mounts = []
[[config.domains]]
serviceName = "web"
port = 8080
@@ -20,14 +20,4 @@ DB_NAME = "${db_name}"
DB_USER = "${db_user}"
DB_PASSWORD = "${db_password}"
# Persist uploads/attachments
[[config.mounts]]
serviceName = "web"
volumeName = "kitchenowl_files"
mountPath = "/data"
# Persist Postgres data
[[config.mounts]]
serviceName = "db"
volumeName = "kitchenowl_db"
mountPath = "/var/lib/postgresql/data"
# Persist uploads/attachments
+1 -5
View File
@@ -4,7 +4,7 @@ secret_key = "${password:64}"
auth_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "frontend"
port = 3000
@@ -28,7 +28,3 @@ LETTERFEED_AUTO_ADD_NEW_SENDERS = "false"
LETTERFEED_SECRET_KEY = "${secret_key}"
LETTERFEED_AUTH_USERNAME = "admin"
LETTERFEED_AUTH_PASSWORD = "${auth_password}"
[[config.mounts]]
name = "letterfeed_data"
mountPath = "/data"
+1 -8
View File
@@ -4,6 +4,7 @@ admin_email = "${email}"
mysql_root_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "linkstack"
port = 80
@@ -18,11 +19,3 @@ LOG_LEVEL = "info"
PHP_MEMORY_LIMIT = "256M"
UPLOAD_MAX_FILESIZE = "8M"
MYSQL_ROOT_PASSWORD = "${mysql_root_password}"
[[config.mounts]]
volume = "linkstack-data"
target = "/htdocs"
[[config.mounts]]
volume = "mysql-data"
target = "/var/lib/mysql"
+1 -2
View File
@@ -29,6 +29,7 @@ novita_api_key = ""
infinity_api_key = ""
[config]
mounts = []
[[config.domains]]
serviceName = "litellm"
port = 4000
@@ -63,5 +64,3 @@ ANTHROPIC_API_KEY = "${anthropic_api_key}"
INFISICAL_TOKEN = "${infisical_token}"
NOVITA_API_KEY = "${novita_api_key}"
INFINITY_API_KEY = "${infinity_api_key}"
[[config.mounts]]
+1 -1
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "memos"
port = 5230
@@ -11,4 +12,3 @@ host = "${main_domain}"
[config.env]
MEMOS_MODE = "prod"
MEMOS_PORT = "5230"
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "metube"
port = 8081
@@ -39,5 +40,3 @@ DOWNLOAD_MODE = "limited"
MAX_CONCURRENT_DOWNLOADS = "3"
LOGLEVEL = "INFO"
ENABLE_ACCESSLOG = "false"
[[config.mounts]]
+1 -2
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
mx_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "mixpost"
port = 80
@@ -19,5 +20,3 @@ DB_DATABASE="mixpost_db"
DB_USERNAME="mixpost_user"
DB_PASSWORD="${mx_password}"
[[config.mounts]]
+1 -6
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "morphos-server"
port = 8080
@@ -11,9 +12,3 @@ certResolver = "letsencrypt"
[config.env]
# No environment variables required based on provided information
[[config.mounts]]
source = "../files/morphos-upload"
target = "/upload"
type = "bind"
readOnly = false
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "navidrome"
port = 4533
host = "${main_domain}"
[config.env]
[[config.mounts]]
+1 -2
View File
@@ -4,6 +4,7 @@ admin_password = "${password:16}"
user_password = "${password:16}"
[config]
mounts = []
[[config.domains]]
serviceName = "neko"
port = 8080
@@ -18,5 +19,3 @@ NEKO_MEMBER_MULTIUSER_USER_PASSWORD = "${user_password}"
NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD = "${admin_password}"
NEKO_WEBRTC_EPR = "52000-52100"
NEKO_WEBRTC_ICELITE = "1"
[[config.mounts]]
+1 -10
View File
@@ -5,6 +5,7 @@ postgres_password = "${password:32}"
postgres_db = "root_db"
[config]
mounts = []
[[config.domains]]
serviceName = "nocodb"
port = 8080
@@ -14,13 +15,3 @@ host = "${main_domain}"
POSTGRES_USER = "${postgres_user}"
POSTGRES_PASSWORD = "${postgres_password}"
POSTGRES_DB = "${postgres_db}"
[[config.mounts]]
serviceName = "nocodb"
volumeName = "nc_data"
mountPath = "/usr/app/data"
[[config.mounts]]
serviceName = "root_db"
volumeName = "db_data"
mountPath = "/var/lib/postgresql/data"
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "omni-tools"
port = 80
@@ -10,5 +11,3 @@ host = "${main_domain}"
[config.env]
# API Key
LOCIZE_API_KEY = ""
[[config.mounts]]
+1 -14
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
webui_secret_key = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "open-webui"
port = 8080
@@ -12,17 +13,3 @@ host = "${main_domain}"
# Secret key for signing JWTs for authentication.
# A random key is generated by default.
webui_secret_key = "${webui_secret_key}" # API Key
[[config.mounts]]
serviceName = "open-webui"
type = "volume"
source = "open-webui"
target = "/app/backend/data"
# Mount for the optional 'ollama' service.
# This will be used if you uncomment the 'ollama' service in the docker-compose file.
[[config.mounts]]
serviceName = "ollama"
type = "volume"
source = "ollama"
target = "/root/.ollama"
+1 -4
View File
@@ -3,6 +3,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "opengist"
port = 6157
@@ -12,7 +13,3 @@ host = "${main_domain}"
UID = "1001"
GID = "1001"
OG_LOG_LEVEL = "info"
[[config.mounts]]
# This template uses a named volume defined in the docker-compose.yml,
# so no file mounts need to be configured here.
+1 -20
View File
@@ -7,6 +7,7 @@ allowed_origin = "http://${main_domain}"
next_public_api_base_url = "/api"
[config]
mounts = []
[[config.domains]]
serviceName = "openinary"
port = 3000
@@ -18,23 +19,3 @@ BETTER_AUTH_SECRET = "${better_auth_secret}"
BETTER_AUTH_URL = "${better_auth_url}"
ALLOWED_ORIGIN = "${allowed_origin}"
NEXT_PUBLIC_API_BASE_URL = "${next_public_api_base_url}"
[[config.mounts]]
serviceName = "openinary"
volumeName = "cache-data"
mountPath = "/app/apps/api/cache"
[[config.mounts]]
serviceName = "openinary"
volumeName = "public-files"
mountPath = "/app/apps/api/public"
[[config.mounts]]
serviceName = "openinary"
volumeName = "db-data"
mountPath = "/app/data"
[[config.mounts]]
serviceName = "openinary"
volumeName = "db-data"
mountPath = "/app/web-standalone/data"
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "openspeedtest"
port = 3000
host = "${main_domain}"
[config.env]
[[config.mounts]]
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "owncast"
port = 8080
host = "${main_domain}"
[config.env]
[[config.mounts]]
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "palmr"
port = 5487
@@ -9,5 +10,3 @@ host = "${main_domain}"
[config.env]
ENCRYPTION_KEY = ""
[[config.mounts]]
+1 -12
View File
@@ -7,6 +7,7 @@ email_user = "noreply@example.com"
email_pass = "${password:16}"
[config]
mounts = []
[[config.domains]]
serviceName = "passbolt"
port = 80
@@ -27,15 +28,3 @@ PASSBOLT_EMAIL_PORT = "587"
PASSBOLT_EMAIL_USER = "${email_user}"
PASSBOLT_EMAIL_PASS = "${email_pass}"
PASSBOLT_EMAIL_TLS = "true"
[[config.mounts]]
volume = "gpg_volume"
target = "/etc/passbolt/gpg"
[[config.mounts]]
volume = "jwt_volume"
target = "/etc/passbolt/jwt"
[[config.mounts]]
volume = "passbolt_mariadb_data"
target = "/var/lib/mysql"
+1 -2
View File
@@ -5,6 +5,7 @@ mysql_password = "${password:16}"
mysql_root_password = "${password:20}"
[config]
mounts = []
[[config.domains]]
serviceName = "paymenter"
port = 80
@@ -18,5 +19,3 @@ MYSQL_PASSWORD = "${mysql_password}"
MYSQL_ROOT_PASSWORD = "${mysql_root_password}"
MYSQL_DATABASE = "paymenter"
MYSQL_USER = "paymenter"
[[config.mounts]]
+1 -2
View File
@@ -180,5 +180,4 @@ services:
restart: always
expose:
- '1025'
expose:
- 1080
- '1080'
+1 -6
View File
@@ -5,7 +5,7 @@ admin_password = "${password:32}"
jwt_secret = "${jwt:jwt_secret}"
[config]
mounts = []
[[config.domains]]
serviceName = "picsur"
port = 8080
@@ -16,8 +16,3 @@ host = "${main_domain}"
"PICSUR_ADMIN_PASSWORD" = "${admin_password}"
"PICSUR_JWT_SECRET" = "${jwt_secret}"
"POSTGRES_PASSWORD" = "${postgres_password}"
[[config.mounts]]
name = "picsur-data"
serviceName = "picsur_postgres"
mountPath = "/var/lib/postgresql/data"
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "plark"
port = 80
host = "${main_domain}"
[config.env]
[[config.mounts]]
+1 -4
View File
@@ -4,6 +4,7 @@ admin_email = "${email}"
admin_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "pocketbase"
port = 8090
@@ -12,7 +13,3 @@ host = "${main_domain}"
[config.env]
ADMIN_EMAIL = "${admin_email}"
ADMIN_PASSWORD = "${admin_password}"
[[config.mounts]]
name = "pocketbase-data"
mountPath = "/pocketbase"
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "portainer"
port = 9000
host = "${main_domain}"
[config.env]
[[config.mounts]]
+1 -2
View File
@@ -5,6 +5,7 @@ postgres_password = "${password:32}"
image_tag = "latest"
[config]
mounts = []
[[config.domains]]
serviceName = "rote-frontend"
port = 80
@@ -19,5 +20,3 @@ host = "${backend_domain}"
POSTGRES_PASSWORD = "${postgres_password}"
IMAGE_TAG = "${image_tag}"
VITE_API_BASE = "http://${backend_domain}"
[[config.mounts]]
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "rss-bridge"
port = 80
host = "${main_domain}"
[config.env]
[[config.mounts]]
+1
View File
@@ -1,3 +1,4 @@
# dokploy: allow-host-ports — RustDesk relay/rendezvous protocol ports (21115-21119) must be host-published; Traefik cannot route raw TCP/UDP.
services:
hbbs:
image: rustdesk/rustdesk-server:latest
+1 -3
View File
@@ -3,12 +3,10 @@ server_domain = "${domain}"
encryption_key = "${password:32}"
[config]
mounts = []
[config.env]
RELAY_HOST = "${server_domain}"
RUSTDESK_RELAY_SERVER = "${server_domain}:21117"
RUSTDESK_API_SERVER = "http://${server_domain}:21118"
RUSTDESK_ID_SERVER = "${server_domain}:21116"
ENCRYPTION_KEY = "${encryption_key}"
[[config.mounts]]
+1 -15
View File
@@ -6,6 +6,7 @@ incoming_port = "${INCOMING_PORT:-50000}"
dht_port = "${DHT_PORT:-6881}"
[config]
mounts = []
# Dokploy will route this domain to the container's internal port 8080
[[config.domains]]
serviceName = "rutorrent"
@@ -17,18 +18,3 @@ PUID = "${puid}"
PGID = "${pgid}"
INCOMING_PORT = "${incoming_port}"
DHT_PORT = "${dht_port}"
[[config.mounts]]
type = "volume"
source = "rutorrent-data"
target = "/data"
[[config.mounts]]
type = "volume"
source = "rutorrent-downloads"
target = "/downloads"
[[config.mounts]]
type = "volume"
source = "rutorrent-passwd"
target = "/passwd"
+3 -1
View File
@@ -27,7 +27,7 @@ services:
retries: 5
senddock:
image: ghcr.io/arkhe-systems/senddock:latest
image: ghcr.io/arkhe-systems/senddock:0.6.5.1
restart: unless-stopped
depends_on:
postgres:
@@ -40,6 +40,8 @@ services:
- JWT_SECRET=${JWT_SECRET}
- PUBLIC_URL=${PUBLIC_URL}
- FRONTEND_URL=${PUBLIC_URL}
- DEPLOYMENT_MODE=self-hosted
- SENDDOCK_LICENSE_KEY=${SENDDOCK_LICENSE_KEY}
- PORT=8080
expose:
- 8080
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "senddock",
"name": "SendDock",
"version": "0.8.0",
"version": "0.6.5.1",
"description": "Open-source, self-hosted email marketing platform with BYO SMTP. Manage subscribers, templates, broadcasts, and scheduled campaigns from one place.",
"logo": "senddock.svg",
"links": {
+2 -1
View File
@@ -7,7 +7,8 @@ jwt_secret = "${password:64}"
env = [
"POSTGRES_PASSWORD=${postgres_password}",
"JWT_SECRET=${jwt_secret}",
"PUBLIC_URL=https://${main_domain}"
"PUBLIC_URL=https://${main_domain}",
"SENDDOCK_LICENSE_KEY="
]
[[config.domains]]
+1 -4
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
sb_user = "admin:${password:16}"
[config]
mounts = []
[[config.domains]]
serviceName = "silverbullet"
port = 3000
@@ -10,7 +11,3 @@ host = "${main_domain}"
[config.env]
SB_USER = "${sb_user}"
[[config.mounts]]
name = "silverbullet-space"
mountPath = "/space"
+1 -4
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "statping-ng"
port = 8080
@@ -10,7 +11,3 @@ host = "${main_domain}"
[config.env]
TZ = "UTC"
DB_CONN = "sqlite"
[[config.mounts]]
source = "../files/statping-ng"
target = "/app"
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "storyden"
port = 8000
@@ -9,5 +10,3 @@ host = "${main_domain}"
[config.env]
STORYDEN_FQDN = "http://${main_domain}"
[[config.mounts]]
+1 -2
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
session_secret = "${password:64}"
[config]
mounts = []
[[config.domains]]
serviceName = "streamflow"
port = 7575
@@ -11,5 +12,3 @@ host = "${main_domain}"
[config.env]
SESSION_SECRET = "${session_secret}"
TIMEZONE = "Asia/Jakarta"
[[config.mounts]]
+1 -5
View File
@@ -4,6 +4,7 @@ surrealdb_user = "${username}"
surrealdb_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "surrealdb"
port = 8000
@@ -12,8 +13,3 @@ host = "${main_domain}"
[config.env]
SURREAL_USER = "${surrealdb_user}"
SURREAL_PASS = "${surrealdb_password}"
[[config.mounts]]
serviceName = "surrealdb"
volumeName = "db_data"
mountPath = "/usr/app/data"
+1 -6
View File
@@ -4,13 +4,8 @@ main_domain = "${domain}"
jwt_secret = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "tianji"
port = 12345
host = "${main_domain}"
[[config.mounts]]
serviceName = "postgres"
type = "volume"
source = "tianji-db-data"
target = "/var/lib/postgresql/data"
+1 -2
View File
@@ -6,6 +6,7 @@ keep_app_running = "1"
timezone = "Europe/Vienna"
[config]
mounts = []
[[config.domains]]
serviceName = "tor"
port = 5800
@@ -16,5 +17,3 @@ DISPLAY_WIDTH = "${display_width}"
DISPLAY_HEIGHT = "${display_height}"
KEEP_APP_RUNNING = "${keep_app_running}"
TZ = "${timezone}"
[[config.mounts]]
+1 -5
View File
@@ -3,7 +3,7 @@ main_domain = "${domain}"
upsnap_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "upsnap"
port = 8090
@@ -17,7 +17,3 @@ UPSNAP_SCAN_RANGE = "192.168.1.0/24"
UPSNAP_SCAN_TIMEOUT = "500ms"
UPSNAP_PING_PRIVILEGED = "true"
UPSNAP_WEBSITE_TITLE = "Upsnap Network Monitor"
[[config.mounts]]
source = "upsnap-data"
target = "/app/pb_data"
+1 -10
View File
@@ -4,6 +4,7 @@ db_password = "${password:32}"
jwt_secret = "${jwt:db_password}"
[config]
mounts = []
[[config.domains]]
serviceName = "vikunja"
port = 3456
@@ -17,13 +18,3 @@ VIKUNJA_DATABASE_TYPE = "postgres"
VIKUNJA_DATABASE_USER = "vikunja"
VIKUNJA_DATABASE_DATABASE = "vikunja"
VIKUNJA_SERVICE_JWTSECRET = "${jwt_secret}"
[[config.mounts]]
serviceName = "vikunja"
volumeName = "vikunja-files"
mountPath = "/app/vikunja/files"
[[config.mounts]]
serviceName = "db"
volumeName = "vikunja-db"
mountPath = "/var/lib/postgresql/data"
+1 -17
View File
@@ -6,7 +6,7 @@ upload_user = "${username}"
upload_password = "${password:16}"
[config]
mounts = []
[[config.domains]]
serviceName = "web"
port = 3000
@@ -28,19 +28,3 @@ POCKETBASE_ENCRYPTION_KEY = "${pocketbase_key}"
UPLOAD_USER = "${upload_user}"
UPLOAD_PASSWORD = "${upload_password}"
ORIGIN = "http://${main_domain}"
[[config.mounts]]
serviceName = "search"
volumeName = "search-data"
mountPath = "/meili_data/data.ms"
[[config.mounts]]
serviceName = "db"
volumeName = "db-data"
mountPath = "/pb_data"
[[config.mounts]]
serviceName = "web"
volumeName = "uploads"
mountPath = "/app/uploads"
+1 -2
View File
@@ -2,6 +2,7 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "web-check"
port = 3000
@@ -27,5 +28,3 @@ API_TIMEOUT_LIMIT = "10000"
API_CORS_ORIGIN = "*"
API_ENABLE_RATE_LIMIT = "true"
ENABLE_ANALYTICS = "false"
[[config.mounts]]
-46
View File
@@ -1,46 +0,0 @@
services:
whatomate:
image: shridh0r/whatomate:0.1.1-beta-0.16.1
restart: unless-stopped
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
environment:
- TZ=Etc/UTC
volumes:
- ../files/config.toml:/app/config.toml:ro
- whatomate-uploads:/app/uploads
command: ["server", "-migrate", "-config", "/app/config.toml"]
db:
image: postgres:17-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=whatomate
- POSTGRES_PASSWORD=whatomate
- POSTGRES_DB=whatomate
healthcheck:
test: ["CMD-SHELL", "pg_isready -U whatomate -d whatomate"]
interval: 10s
timeout: 5s
retries: 6
volumes:
- whatomate-postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- whatomate-redis-data:/data
volumes:
whatomate-uploads:
whatomate-postgres-data:
whatomate-redis-data:
-14
View File
@@ -1,14 +0,0 @@
# Whatomate
## Getting started
1. Deploy the template and open the app domain.
2. Log in with the default admin credentials: **admin@admin.com** / **admin**. Change the password right after the first login.
3. Connect your WhatsApp Business account: go to **Settings → WhatsApp Accounts** and add your Meta Cloud API credentials (phone number ID, access token). See the [configuration docs](https://shridarpatil.github.io/whatomate/getting-started/configuration/).
4. Point your Meta webhook to `https://<your-domain>/webhook` and use the `webhook_verify_token` from the generated `config.toml` mount (Dokploy → your service → Advanced → Mounts).
## Notes
- The full application configuration lives in the `config.toml` file mount (JWT secret, encryption key and webhook verify token are auto-generated per deployment). Edit it and redeploy to change settings.
- Uploads are persisted in the `whatomate-uploads` volume; PostgreSQL and Redis data in `whatomate-postgres-data` and `whatomate-redis-data`.
- Voice calling / IVR requires extra WebRTC configuration (UDP ports, public IP or TURN server). See the [calling docs](https://shridarpatil.github.io/whatomate/features/calling/).
-17
View File
@@ -1,17 +0,0 @@
{
"id": "whatomate",
"name": "Whatomate",
"version": "0.1.1-beta-0.16.1",
"description": "Modern, open-source WhatsApp Business Platform with real-time chat, chatbot automation, bulk campaigns, template management, and voice calling via Meta's Cloud API.",
"logo": "whatomate.svg",
"links": {
"github": "https://github.com/shridarpatil/whatomate",
"website": "https://whatomate.io/",
"docs": "https://shridarpatil.github.io/whatomate/"
},
"tags": [
"communication",
"automation",
"crm"
]
}
-67
View File
@@ -1,67 +0,0 @@
[variables]
main_domain = "${domain}"
jwt_secret = "${password:64}"
encryption_key = "${password:32}"
webhook_verify_token = "${password:32}"
[config]
[[config.domains]]
serviceName = "whatomate"
port = 8_080
host = "${main_domain}"
[[config.mounts]]
filePath = "config.toml"
content = """
[app]
name = "Whatomate"
environment = "staging"
debug = false
encryption_key = "${encryption_key}"
[server]
host = "0.0.0.0"
port = 8080
read_timeout = 30
write_timeout = 30
base_path = ""
allowed_origins = ""
[database]
host = "db"
port = 5432
user = "whatomate"
password = "whatomate"
name = "whatomate"
ssl_mode = "disable"
max_open_conns = 25
max_idle_conns = 5
conn_max_lifetime = 300
[redis]
host = "redis"
port = 6379
username = ""
password = ""
db = 0
tls = false
[jwt]
secret = "${jwt_secret}"
access_expiry_mins = 15
refresh_expiry_days = 1
[storage]
type = "local"
local_path = "./uploads"
[whatsapp]
webhook_verify_token = "${webhook_verify_token}"
api_version = "v24.0"
base_url = "https://graph.facebook.com"
[default_admin]
email = "admin@admin.com"
password = "admin"
full_name = "Admin"
"""
-5
View File
@@ -1,5 +0,0 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" r="18" fill="#25D366"/>
<path d="M20 8C13.373 8 8 13.373 8 20C8 22.385 8.632 24.615 9.745 26.545L8 32L13.655 30.295C15.515 31.295 17.685 31.87 20 31.87C26.627 31.87 32 26.497 32 19.87C32 13.373 26.627 8 20 8Z" fill="white"/>
<path d="M26.5 23.5C26.2 24.5 24.8 25.3 23.7 25.5C22.9 25.6 21.9 25.7 18.5 24.3C14.3 22.5 11.6 18.2 11.4 17.9C11.2 17.6 9.5 15.4 9.5 13.1C9.5 10.8 10.7 9.7 11.2 9.2C11.6 8.8 12.2 8.6 12.7 8.6C12.9 8.6 13.1 8.6 13.2 8.6C13.7 8.6 14 8.7 14.3 9.4C14.7 10.3 15.5 12.6 15.6 12.8C15.7 13 15.8 13.3 15.6 13.6C15.5 13.9 15.4 14.1 15.2 14.3C15 14.5 14.8 14.8 14.6 15C14.4 15.2 14.2 15.4 14.4 15.8C14.6 16.2 15.4 17.5 16.6 18.6C18.1 19.9 19.4 20.4 19.9 20.6C20.4 20.8 20.7 20.8 21 20.5C21.3 20.2 22 19.4 22.3 19C22.6 18.6 23 18.5 23.4 18.7C23.8 18.8 26.1 19.9 26.5 20.1C26.9 20.3 27.2 20.4 27.3 20.6C27.4 20.9 27.4 21.8 26.5 23.5Z" fill="#25D366"/>
</svg>

Before

Width:  |  Height:  |  Size: 997 B

+1 -5
View File
@@ -3,6 +3,7 @@ main_domain = "${domain}"
SECRET = "${password:64}"
[config]
mounts = []
[[config.domains]]
serviceName = "yamtrack"
port = 8000
@@ -12,8 +13,3 @@ host = "${main_domain}"
SECRET = "${SECRET}"
TZ = "Europe/Berlin"
REDIS_URL = "redis://redis:6379"
[[config.mounts]]
serviceName = "yamtrack"
volumeName = "yamtrack-db"
mountPath = "/yamtrack/db"
+1 -2
View File
@@ -2,11 +2,10 @@
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "yt-dlp-webui"
port = 3033
host = "${main_domain}"
[config.env]
[[config.mounts]]
+19 -3
View File
@@ -25,6 +25,7 @@ interface ValidationResult {
type LogLevel = "info" | "success" | "warning" | "error" | "debug";
class DockerComposeValidator {
private allowHostPorts = false;
private options: Required<DockerComposeValidatorOptions>;
private errors: string[] = [];
private warnings: string[] = [];
@@ -73,6 +74,9 @@ class DockerComposeValidator {
}
const content = fs.readFileSync(composePath, "utf8");
// Opt-out marker for templates whose protocols require host-published ports
// (mail/SMTP, game servers, streaming, VPN, remote-desktop relays...).
this.allowHostPorts = /^#\s*dokploy:\s*allow-host-ports\b/m.test(content);
const compose = yaml.parse(content) as ComposeSpecification;
if (!compose || typeof compose !== "object") {
@@ -172,13 +176,25 @@ class DockerComposeValidator {
if (typeof port === "string") {
// Check for port mapping format (e.g., "3000:3000" or "8080:80")
if (/^\d+:\d+/.test(port)) {
this.error(
`Service '${serviceName}': ports[${index}] uses port mapping format '${port}'. According to README, use only port number (e.g., '3000') instead of '3000:3000'. Dokploy handles port routing.`
);
if (this.allowHostPorts) {
this.warning(
`Service '${serviceName}': ports[${index}] publishes host port '${port}' (allowed by '# dokploy: allow-host-ports' marker)`
);
} else {
this.error(
`Service '${serviceName}': ports[${index}] uses port mapping format '${port}'. According to README, use only port number (e.g., '3000') instead of '3000:3000'. Dokploy handles port routing.`
);
}
}
} else if (typeof port === "object" && port !== null) {
// Check for published port mapping
if (port.published && port.target) {
if (this.allowHostPorts) {
this.warning(
`Service '${serviceName}': ports[${index}] publishes host port (allowed by '# dokploy: allow-host-ports' marker)`
);
return;
}
this.error(
`Service '${serviceName}': ports[${index}] uses port mapping (published: ${port.published}, target: ${port.target}). According to README, use only port number. Dokploy handles port routing.`
);