terraform { required_providers { docker = { source = "kreuzwerker/docker" version = "~> 3.9" } } } locals { persistent = var.environment == "prod" # Inject oauth2 token into git URL for private repos git_repo_url = ( var.openresty_git_token != "" ? replace(var.openresty_git_repo, "://", "://oauth2:${var.openresty_git_token}@") : var.openresty_git_repo ) # Entrypoint for git_clone mode: installs git via apk, clones the pinned ref, # then hands off to openresty. The cloned repo must have an 'openresty/' subdirectory # containing a valid nginx.conf (used as the -p prefix path). git_clone_entrypoint = [ "/bin/sh", "-c", "apk add --no-cache git && git clone --depth 1 --branch '${var.openresty_git_ref}' '${local.git_repo_url}' /tmp/app && exec openresty -g 'daemon off;' -p /tmp/app/openresty" ] } # ─── Network ────────────────────────────────────────────────────────────────── # Each app gets its own isolated bridge network. Postgres and Redis are not # exposed externally; only OpenResty has a published port. resource "docker_network" "app" { name = "${var.app_name}-network" driver = "bridge" } # ─── Volumes (prod only) ────────────────────────────────────────────────────── # In dev mode containers are ephemeral; volumes are created only for prod. resource "docker_volume" "postgres" { count = local.persistent ? 1 : 0 name = "${var.app_name}-postgres-data" } resource "docker_volume" "redis" { count = local.persistent ? 1 : 0 name = "${var.app_name}-redis-data" } # ─── Images ─────────────────────────────────────────────────────────────────── # Standard OpenResty image — used for bind_mount and git_clone modes. resource "docker_image" "openresty" { count = var.openresty_source_type != "local_build" ? 1 : 0 name = var.openresty_image keep_locally = true } # Custom-built OpenResty image — used for local_build mode. # The build context is transferred from the local machine to the remote Docker # daemon over SSH and built there. Rebuilds are triggered by changes to any # file in the context directory. resource "docker_image" "openresty_custom" { count = var.openresty_source_type == "local_build" ? 1 : 0 name = "${var.app_name}-openresty:latest" keep_locally = true build { context = var.openresty_local_build_context dockerfile = var.openresty_dockerfile } triggers = { # Rebuild when any file in the build context changes. context_hash = sha1(join("", [ for f in sort(fileset(var.openresty_local_build_context, "**/*")) : filesha1("${var.openresty_local_build_context}/${f}") ])) } } resource "docker_image" "postgres" { name = var.postgres_image keep_locally = true } resource "docker_image" "redis" { name = var.redis_image keep_locally = true } # ─── PostgreSQL ─────────────────────────────────────────────────────────────── resource "docker_container" "postgres" { image = docker_image.postgres.image_id name = "${var.app_name}-postgres" restart = "unless-stopped" networks_advanced { name = docker_network.app.name } env = [ "POSTGRES_DB=${var.db_name}", "POSTGRES_USER=${var.db_user}", "POSTGRES_PASSWORD=${var.db_password}", ] dynamic "volumes" { for_each = local.persistent ? [1] : [] content { volume_name = docker_volume.postgres[0].name container_path = "/var/lib/postgresql/data" } } } # ─── Redis ──────────────────────────────────────────────────────────────────── resource "docker_container" "redis" { image = docker_image.redis.image_id name = "${var.app_name}-redis" restart = "unless-stopped" networks_advanced { name = docker_network.app.name } dynamic "volumes" { for_each = local.persistent ? [1] : [] content { volume_name = docker_volume.redis[0].name container_path = "/data" } } } # ─── OpenResty ──────────────────────────────────────────────────────────────── resource "docker_container" "openresty" { image = ( var.openresty_source_type == "local_build" ? docker_image.openresty_custom[0].image_id : docker_image.openresty[0].image_id ) name = "${var.app_name}-openresty" restart = "unless-stopped" networks_advanced { name = docker_network.app.name } ports { internal = 80 external = var.openresty_external_port } # bind_mount: mount a pre-existing directory from the remote host. # The directory must contain a valid nginx.conf and any Lua files. dynamic "volumes" { for_each = var.openresty_source_type == "bind_mount" ? [1] : [] content { host_path = var.openresty_remote_config_path container_path = "/usr/local/openresty/nginx/conf" read_only = true } } # git_clone: override the container entrypoint to clone the repo and start openresty. # The base image must support apk (Alpine). The cloned repo must have an openresty/ # subdirectory with nginx.conf. entrypoint = var.openresty_source_type == "git_clone" ? local.git_clone_entrypoint : null # Expose service connection info as env vars so Lua code can use them via os.getenv(). env = [ "APP_NAME=${var.app_name}", "POSTGRES_HOST=${var.app_name}-postgres", "POSTGRES_DB=${var.db_name}", "POSTGRES_USER=${var.db_user}", "POSTGRES_PASSWORD=${var.db_password}", "REDIS_HOST=${var.app_name}-redis", ] depends_on = [ docker_container.postgres, docker_container.redis, ] }