name: dokploy-template-toml description: "Generate template.toml configuration for Dokploy templates with variables, domains, environment mappings, and file mounts. Use when finalizing Dokploy templates." version: 1.0.0 author: Home Lab Infrastructure Team
Dokploy Template TOML
When to Use This Skill
- When creating template.toml for a new Dokploy template
- When configuring Dokploy variable generation (passwords, secrets)
- When setting up domain configuration for services
- When adding file mounts for configuration files
- When user asks about "dokploy variables" or "template configuration"
When NOT to Use This Skill
- When working with docker-compose only (no Dokploy)
- For runtime configuration changes (modify environment vars instead)
Prerequisites
- Completed docker-compose.yml file
- List of required/optional environment variables
- Domain configuration requirements
- Knowledge of secret generation needs
Template.toml Structure
# Header comment with application info
# Application Name - Description
# https://github.com/example/application
[variables]
# User-provided and auto-generated variables
domain = "${domain}"
secret_key = "${base64:64}"
password = "${password:32}"
# Domain configuration for Traefik
[[config.domains]]
serviceName = "app"
port = 3000
host = "${domain}"
# Environment variables for all services
[config.env]
APP_DOMAIN = "${domain}"
APP_SECRET = "${secret_key}"
# Optional file mounts
[[config.mounts]]
serviceName = "app"
mountPath = "/app/config.json"
content = """
{
"setting": "${variable}"
}
"""
Core Patterns
Pattern 1: Variable Definitions
[variables]
# User-provided domain (required)
domain = "${domain}"
# Random password generation
db_password = "${password:32}" # 32-char alphanumeric
api_key = "${password:48}" # 48-char alphanumeric
# Base64 secret generation
secret_key = "${base64:64}" # 64-char base64 encoded
jwt_secret = "${base64:32}" # 32-char base64 encoded
# UUID generation
instance_id = "${uuid}" # Random UUID v4
# User inputs
admin_email = "${email}" # User-provided email
username = "${username}" # User-provided username
# Derived variables (string concatenation)
app_url = "https://${domain}"
db_url = "postgresql://user:${db_password}@postgres:5432/db"
Supported Variable Types:
| Syntax | Description | Example Output |
|---|---|---|
${domain} | User-provided domain | example.com |
${password:N} | N-char random password | aB3kL9mN... |
${base64:N} | N-char base64 secret | dGhpcyBpcyBh... |
${uuid} | Random UUID v4 | 550e8400-e29b-41d4-a716-446655440000 |
${username} | User-provided username | admin |
${email} | User-provided email | user@example.com |
Pattern 2: Domain Configuration
# Single domain
[[config.domains]]
serviceName = "app"
port = 3000
host = "${domain}"
# Multiple domains (different services)
[[config.domains]]
serviceName = "app"
port = 3000
host = "${domain}"
[[config.domains]]
serviceName = "api"
port = 8080
host = "api.${domain}"
# Admin subdomain
[[config.domains]]
serviceName = "admin"
port = 9000
host = "admin.${domain}"
Important: serviceName must match the service name in docker-compose.yml
Pattern 3: Environment Variables
[config.env]
# Required variables (error if not set in compose)
APP_DOMAIN = "${domain}"
APP_SECRET = "${secret_key}"
DATABASE_PASSWORD = "${db_password}"
# Categories with comments
# ===========================================
# Database Configuration
# ===========================================
POSTGRES_DB = "appdb"
POSTGRES_USER = "appuser"
POSTGRES_PASSWORD = "${db_password}"
# ===========================================
# Application Settings
# ===========================================
APP_URL = "https://${domain}"
DEBUG = "false"
# ===========================================
# External Services (Cloudflare R2)
# ===========================================
S3_ENDPOINT = ""
S3_ACCESS_KEY_ID = ""
S3_SECRET_ACCESS_KEY = ""
S3_BUCKET = ""
S3_REGION = "auto"
Pattern 4: File Mounts
# JSON configuration file
[[config.mounts]]
serviceName = "app"
mountPath = "/app/config.json"
content = """
{
"domain": "${domain}",
"secret": "${secret_key}",
"database": {
"host": "postgres",
"port": 5432,
"name": "appdb"
}
}
"""
# INI/conf file
[[config.mounts]]
serviceName = "app"
mountPath = "/etc/app/settings.conf"
content = """
[general]
domain = ${domain}
debug = false
[database]
host = postgres
port = 5432
password = ${db_password}
"""
# YAML configuration
[[config.mounts]]
serviceName = "app"
mountPath = "/app/config.yaml"
content = """
server:
host: 0.0.0.0
port: 3000
domain: ${domain}
security:
secret_key: ${secret_key}
"""
Complete Examples
Example 1: Simple Web App (Paaster)
# Paaster - End-to-end encrypted pastebin
# https://github.com/WardPearce/paaster
[variables]
domain = "${domain}"
cookie_secret = "${base64:64}"
[[config.domains]]
serviceName = "paaster"
port = 3000
host = "${domain}"
[config.env]
# ===========================================
# Application Domain
# ===========================================
PAASTER_DOMAIN = "${domain}"
# ===========================================
# Security
# ===========================================
COOKIE_SECRET = "${cookie_secret}"
# ===========================================
# MongoDB Configuration
# ===========================================
MONGO_DB = "paasterv3"
# ===========================================
# Cloudflare R2 Storage
# Get from: Cloudflare Dashboard > R2 > Manage R2 API Tokens
# Endpoint format: https://<ACCOUNT_ID>.r2.cloudflarestorage.com
# ===========================================
S3_ENDPOINT = ""
S3_ACCESS_KEY_ID = ""
S3_SECRET_ACCESS_KEY = ""
S3_BUCKET = ""
S3_REGION = "auto"
Example 2: Git Service with Database (Forgejo)
# Forgejo - Self-hosted Git forge
# https://forgejo.org/
[variables]
domain = "${domain}"
postgres_password = "${password:32}"
secret_key = "${base64:64}"
internal_token = "${base64:48}"
jwt_secret = "${base64:48}"
[[config.domains]]
serviceName = "forgejo"
port = 3000
host = "${domain}"
[config.env]
# ===========================================
# Domain Configuration
# ===========================================
FORGEJO__server__DOMAIN = "${domain}"
FORGEJO__server__ROOT_URL = "https://${domain}/"
# ===========================================
# Database Configuration
# ===========================================
FORGEJO__database__DB_TYPE = "postgres"
FORGEJO__database__HOST = "postgres:5432"
FORGEJO__database__NAME = "forgejo"
FORGEJO__database__USER = "forgejo"
FORGEJO__database__PASSWD = "${postgres_password}"
# PostgreSQL credentials
POSTGRES_USER = "forgejo"
POSTGRES_DB = "forgejo"
POSTGRES_PASSWORD = "${postgres_password}"
# ===========================================
# Security Keys
# ===========================================
FORGEJO__security__SECRET_KEY = "${secret_key}"
FORGEJO__security__INTERNAL_TOKEN = "${internal_token}"
FORGEJO__oauth2__JWT_SECRET = "${jwt_secret}"
# ===========================================
# Optional: SSH Configuration
# ===========================================
SSH_PORT = "2222"
Example 3: Complex Stack (Paperless-ngx)
# Paperless-ngx - Document management system
# https://docs.paperless-ngx.com/
[variables]
domain = "${domain}"
postgres_password = "${password:32}"
secret_key = "${base64:64}"
admin_user = "${username}"
admin_password = "${password:16}"
admin_email = "${email}"
[[config.domains]]
serviceName = "paperless"
port = 8000
host = "${domain}"
[config.env]
# ===========================================
# Database Configuration
# ===========================================
POSTGRES_DB = "paperless"
POSTGRES_USER = "paperless"
POSTGRES_PASSWORD = "${postgres_password}"
# Paperless database settings
PAPERLESS_DBHOST = "postgres"
PAPERLESS_DBNAME = "paperless"
PAPERLESS_DBUSER = "paperless"
PAPERLESS_DBPASS = "${postgres_password}"
# ===========================================
# Application Settings
# ===========================================
PAPERLESS_SECRET_KEY = "${secret_key}"
PAPERLESS_URL = "https://${domain}"
PAPERLESS_ALLOWED_HOSTS = "${domain}"
PAPERLESS_CORS_ALLOWED_HOSTS = "https://${domain}"
# ===========================================
# Admin User (created on first run)
# ===========================================
PAPERLESS_ADMIN_USER = "${admin_user}"
PAPERLESS_ADMIN_PASSWORD = "${admin_password}"
PAPERLESS_ADMIN_MAIL = "${admin_email}"
# ===========================================
# Document Processing
# ===========================================
PAPERLESS_OCR_LANGUAGE = "eng"
PAPERLESS_TIKA_ENABLED = "1"
PAPERLESS_TIKA_ENDPOINT = "http://tika:9998"
PAPERLESS_TIKA_GOTENBERG_ENDPOINT = "http://gotenberg:3000"
# ===========================================
# Redis
# ===========================================
PAPERLESS_REDIS = "redis://redis:6379"
Example 4: Service with Config File Mount
# ANyONe Protocol Relay
# https://github.com/anyone-protocol/ator-protocol
[variables]
relay_nickname = "${username}"
relay_contact = "${email}"
control_password = "${password:24}"
orport = "9001"
dirport = "9030"
controlport = "9051"
wallet_address = ""
[config.env]
ACCEPT_TOS = "1"
ANON_NICKNAME = "${relay_nickname}"
ANON_CONTACT = "${relay_contact}"
ANON_ORPORT = "${orport}"
ANON_DIRPORT = "${dirport}"
ANON_CONTROLPORT = "${controlport}"
[[config.mounts]]
serviceName = "anon-relay"
mountPath = "/etc/anon/anonrc"
content = """
# ANyONe Protocol Relay Configuration
# Auto-generated by Dokploy
# Relay Identity
Nickname ${relay_nickname}
ContactInfo ${relay_contact}
# Network Ports
ORPort ${orport}
DirPort ${dirport}
ControlPort ${controlport}
# Control Authentication
HashedControlPassword ${control_password}
# Relay Type (exit disabled by default)
ExitRelay 0
# Bandwidth Limits (optional)
# RelayBandwidthRate 1 MB
# RelayBandwidthBurst 2 MB
"""
Quality Standards
Mandatory Requirements
- All required variables defined in
[variables] - Domain configuration matches docker-compose service names
- Environment variables organized with category comments
- Sensitive values use auto-generation (password, base64)
- User-facing values use appropriate prompts (domain, email)
Documentation Standards
- Add header comment with app name and link
- Comment each category in
[config.env] - Document where to get external credentials (R2, etc.)
Security Standards
- Never hardcode passwords or secrets
- Use
${password:N}for database passwords - Use
${base64:N}for encryption keys - Leave external API keys blank for user input
Common Pitfalls
Pitfall 1: serviceName mismatch
Issue: Domain config doesn't work
Solution: serviceName must exactly match docker-compose service name
Pitfall 2: Missing variable definitions
Issue: Variables referenced but not defined
Solution: Define all ${var} references in [variables] section
Pitfall 3: Password in plain text
Issue: Secrets visible in Dokploy UI
Solution: Use ${password:N} or ${base64:N} for auto-generation
Pitfall 4: Wrong mount serviceName
Issue: Config file not mounted Solution: Match serviceName to exact docker-compose service
Integration
Skills-First Approach (v2.0+)
This skill is part of the skills-first architecture - loaded progressively during the Generation phase as the final step before validation.
Related Skills
dokploy-compose-structure: Service names for domain configdokploy-environment-config: Environment variable patternsdokploy-cloudflare-integration: R2 configuration
Invoked By
/dokploy-createcommand: Phase 3 (Generation) - Step 6 (Final)
Order in Workflow (Progressive Loading)
dokploy-compose-structure: Create compose filedokploy-traefik-routing: Add routingdokploy-health-patterns: Add health checksdokploy-cloudflare-integration: Add CF config (if applicable)dokploy-environment-config: Plan environment vars- This skill: Create template.toml (final generation step)
See: .claude/commands/dokploy-create.md for full workflow