# A complete Dokploy environment: a project, a staging environment, a public # web service with a database and a cache behind it, and a Compose stack. # # export DOKPLOY_HOST=https://dokploy.example.com # export DOKPLOY_API_KEY=... # terraform init && terraform apply terraform { required_version = ">= 1.5" required_providers { dokploy = { source = "maxvojtkov/dokploy" version = ">= 0.1" } random = { source = "hashicorp/random" version = ">= 3.6" } } } provider "dokploy" { # host and api_key are read from DOKPLOY_HOST and DOKPLOY_API_KEY. } # ------------------------------------------------------------------- structure resource "dokploy_project" "shop" { name = "shop" description = "Storefront and its backing services" # Shared by every service in the project. env = "TZ=Europe/Bucharest" } # Dokploy creates a default "production" environment with the project; add a # second one for staging. resource "dokploy_environment" "staging" { name = "staging" description = "Pre-production" project_id = dokploy_project.shop.id env = "LOG_LEVEL=debug" } # -------------------------------------------------------------------- secrets resource "random_password" "postgres" { length = 32 special = false } resource "random_password" "redis" { length = 32 special = false } # ------------------------------------------------------------------- datastores resource "dokploy_postgres" "shop" { name = "shop-db" environment_id = dokploy_project.shop.default_environment_id docker_image = "postgres:16-alpine" database_name = "shop" database_user = "shop" database_password = random_password.postgres.result memory_limit = "1g" cpu_limit = "1" memory_reservation = "256m" } resource "dokploy_redis" "sessions" { name = "shop-cache" environment_id = dokploy_project.shop.default_environment_id docker_image = "redis:7-alpine" database_password = random_password.redis.result memory_limit = "256m" } # --------------------------------------------------------------- the web service module "storefront" { source = "../../modules/web-service" name = "storefront" description = "Public storefront" environment_id = dokploy_project.shop.default_environment_id service_source = { type = "github" github_id = var.github_provider_id owner = "acme" repository = "storefront" branch = "main" } build_type = "nixpacks" auto_deploy = true replicas = 2 # Dokploy resolves service names on the shared Docker network, so the # database is reachable by its generated app_name. env = join("\n", [ "DATABASE_URL=postgresql://shop:${random_password.postgres.result}@${dokploy_postgres.shop.app_name}:5432/shop", "REDIS_URL=redis://:${random_password.redis.result}@${dokploy_redis.sessions.app_name}:6379", "NODE_ENV=production", ]) resources = { memory_limit = "1g" cpu_limit = "1" } domains = [ { host = "shop.example.com" port = 3000 }, ] redirects = [ { regex = "^https://www\\.shop\\.example\\.com/(.*)" replacement = "https://shop.example.com/$${1}" permanent = true }, ] mounts = [ { mount_path = "/app/uploads" type = "volume" volume_name = "storefront-uploads" }, ] } # -------------------------------------------- an internal tool behind basic auth module "admin" { source = "../../modules/web-service" name = "admin" environment_id = dokploy_environment.staging.id service_source = { type = "docker" image = "traefik/whoami:latest" } domains = [ { host = "admin.staging.example.com" port = 80 }, ] basic_auth = { username = "ops" password = var.admin_password } } # --------------------------------------------------------------- a compose stack resource "dokploy_compose" "observability" { name = "observability" environment_id = dokploy_project.shop.default_environment_id compose_type = "docker-compose" source_type = "raw" # Preserve volumes if this stack is ever destroyed. delete_volumes = false compose_file = <<-YAML services: uptime: image: louislam/uptime-kuma:1 volumes: - uptime-data:/app/data restart: unless-stopped volumes: uptime-data: YAML } resource "dokploy_domain" "observability" { compose_id = dokploy_compose.observability.id domain_type = "compose" service_name = "uptime" host = "status.example.com" port = 3001 https = true certificate_type = "letsencrypt" }