diff --git a/digitalocean/README.md b/digitalocean/README.md index 3901328..c995339 100644 --- a/digitalocean/README.md +++ b/digitalocean/README.md @@ -10,6 +10,62 @@ This Terraform project automates the deployment of Fleet Device Management (Flee * **DNS** — Domain and CNAME record for Fleet * **Database Firewalls** — Restricts database and cache access to the App Platform app only +## Using Your Own Image + +By default, the module deploys the official `fleetdm/fleet` image from Docker Hub. To deploy your own image (e.g., a custom Fleet build), set `image_tag` in `fleet_config` to a full image reference: + +### Docker Hub (public) + +```hcl +fleet_config = { + image_tag = "your-org/your-fleet:v1.0.0" + # ... +} +``` + +### Docker Hub (private) + +```hcl +fleet_config = { + image_tag = "your-org/your-fleet:v1.0.0" + image_registry_credentials = "your-username:your-access-token" # Don't commit this! + # ... +} +``` + +Pass the credentials at deploy time instead of committing them: + +```bash +terraform apply -var-file="fleet-10.tfvars" \ + -var="fleet_config={image_tag:\"your-org/your-fleet:v1.0.0\",image_registry_credentials:\"$DOCKERHUB_USER:$DOCKERHUB_TOKEN\",instance_size_slug:\"basic-xs\",instance_count:1,debug_logging:false,exec_migration:true}" +``` + +### DigitalOcean Container Registry (DOCR) + +Push your image to DOCR first: + +```bash +# One-time: create a registry and log in +doctl registry create my-registry +doctl registry login + +# Build, tag, and push your image +docker build -t registry.digitalocean.com/my-registry/fleet:v1.0.0 . +docker push registry.digitalocean.com/my-registry/fleet:v1.0.0 +``` + +Then reference it: + +```hcl +fleet_config = { + image_tag = "registry.digitalocean.com/my-registry/fleet:v1.0.0" + image_deploy_on_push = true # Optional: redeploy automatically on every push + # ... +} +``` + +The module detects the registry automatically from the `image_tag` prefix — anything starting with `registry.digitalocean.com/` uses DOCR, everything else uses Docker Hub. The migration job uses the same image. + ## Prerequisites 1. **Terraform:** Version `~> 1.11`. Install from [terraform.io](https://www.terraform.io/downloads.html). diff --git a/digitalocean/byo-project/app.tf b/digitalocean/byo-project/app.tf index 0ef77cd..b82c0cf 100644 --- a/digitalocean/byo-project/app.tf +++ b/digitalocean/byo-project/app.tf @@ -4,6 +4,22 @@ resource "random_password" "private_key" { } locals { + # -------------------------------------------------------------------------- + # Image parsing — supports Docker Hub and DigitalOcean Container Registry + # + # Accepted formats for var.fleet_config.image_tag: + # "fleetdm/fleet:v4.90.0" → Docker Hub (official) + # "your-org/your-image:v1.0.0" → Docker Hub (custom) + # "registry.digitalocean.com//:tag" → DOCR + # -------------------------------------------------------------------------- + image_tag_parts = split(":", var.fleet_config.image_tag) + image_name_part = local.image_tag_parts[0] + image_tag_part = length(local.image_tag_parts) > 1 ? local.image_tag_parts[length(local.image_tag_parts) - 1] : "latest" + image_segments = split("/", local.image_name_part) + image_is_docr = local.image_segments[0] == "registry.digitalocean.com" + image_repository = local.image_segments[length(local.image_segments) - 1] + image_dh_registry = join("/", slice(local.image_segments, 0, length(local.image_segments) - 1)) + base_env_vars = { FLEET_MYSQL_PROTOCOL = "tcp" FLEET_MYSQL_ADDRESS = "${local.mysql_host}:${local.mysql_port}" @@ -60,10 +76,19 @@ resource "digitalocean_app" "fleet" { http_port = 8080 image { - registry_type = "DOCKER_HUB" - registry = "fleetdm" - repository = "fleet" - tag = trimprefix(var.fleet_config.image_tag, "fleetdm/fleet:") + registry_type = local.image_is_docr ? "DOCR" : "DOCKER_HUB" + registry = local.image_is_docr ? null : local.image_dh_registry + repository = local.image_repository + tag = local.image_tag_part + registry_credentials = var.fleet_config.image_registry_credentials + + # Auto-deploy on push is only supported for DOCR + dynamic "deploy_on_push" { + for_each = local.image_is_docr && var.fleet_config.image_deploy_on_push ? [1] : [] + content { + enabled = true + } + } } health_check { @@ -107,10 +132,11 @@ resource "digitalocean_app" "fleet" { instance_size_slug = var.fleet_config.instance_size_slug image { - registry_type = "DOCKER_HUB" - registry = "fleetdm" - repository = "fleet" - tag = trimprefix(var.fleet_config.image_tag, "fleetdm/fleet:") + registry_type = local.image_is_docr ? "DOCR" : "DOCKER_HUB" + registry = local.image_is_docr ? null : local.image_dh_registry + repository = local.image_repository + tag = local.image_tag_part + registry_credentials = var.fleet_config.image_registry_credentials } run_command = "fleet prepare db --no-prompt=true" diff --git a/digitalocean/byo-project/variables.tf b/digitalocean/byo-project/variables.tf index 3f7c93b..9ce4f8a 100644 --- a/digitalocean/byo-project/variables.tf +++ b/digitalocean/byo-project/variables.tf @@ -24,15 +24,17 @@ variable "vpc_config" { } variable "fleet_config" { - description = "Configuration for the Fleet application deployment." + description = "Configuration for the Fleet application deployment. image_tag accepts full image references from Docker Hub or DOCR." type = object({ - image_tag = string - instance_size_slug = string - instance_count = number - license_key = optional(string) - debug_logging = bool - exec_migration = bool - extra_env_vars = optional(map(string)) + image_tag = string + image_registry_credentials = optional(string) + image_deploy_on_push = optional(bool, false) + instance_size_slug = string + instance_count = number + license_key = optional(string) + debug_logging = bool + exec_migration = bool + extra_env_vars = optional(map(string)) }) } diff --git a/digitalocean/extreme-low-cost.tfvars b/digitalocean/extreme-low-cost.tfvars index f9614a8..5126e82 100644 --- a/digitalocean/extreme-low-cost.tfvars +++ b/digitalocean/extreme-low-cost.tfvars @@ -27,7 +27,13 @@ # App Platform — smallest possible instance # ---------------------------------------------------------------------------- fleet_config = { + # Image — use the official Fleet image or your own: + # Official: "fleetdm/fleet:v4.90.0" + # Custom Docker Hub: "your-org/your-fleet:v1.0.0" + # DigitalOcean DOCR: "registry.digitalocean.com/your-registry/fleet:v1.0.0" image_tag = "fleetdm/fleet:v4.90.0" + # image_registry_credentials = "your-username:your-token" # private Docker Hub + # image_deploy_on_push = true # DOCR only instance_size_slug = "basic-xxs" # $5/mo — 512 MiB, 1 shared vCPU instance_count = 1 debug_logging = false diff --git a/digitalocean/fleet-10.tfvars b/digitalocean/fleet-10.tfvars index 510ebbb..4341fcc 100644 --- a/digitalocean/fleet-10.tfvars +++ b/digitalocean/fleet-10.tfvars @@ -27,7 +27,18 @@ # $10/month — plenty for 10 devices # ---------------------------------------------------------------------------- fleet_config = { - image_tag = "fleetdm/fleet:v4.90.0" + # Image — use the official Fleet image or your own: + # Official: "fleetdm/fleet:v4.90.0" + # Custom Docker Hub: "your-org/your-fleet:v1.0.0" + # DigitalOcean DOCR: "registry.digitalocean.com/your-registry/fleet:v1.0.0" + image_tag = "fleetdm/fleet:v4.90.0" + + # For private Docker Hub repos: + # image_registry_credentials = "your-username:your-token" + + # For DOCR: auto-deploy when you push a new image + # image_deploy_on_push = true + instance_size_slug = "basic-xs" # 1 GiB RAM, $10/mo instance_count = 1 debug_logging = false diff --git a/digitalocean/low-cost.tfvars b/digitalocean/low-cost.tfvars index 42e7d89..b2fa560 100644 --- a/digitalocean/low-cost.tfvars +++ b/digitalocean/low-cost.tfvars @@ -20,7 +20,13 @@ # App Platform — same as default, but without Redis env vars # ---------------------------------------------------------------------------- fleet_config = { + # Image — use the official Fleet image or your own: + # Official: "fleetdm/fleet:v4.90.0" + # Custom Docker Hub: "your-org/your-fleet:v1.0.0" + # DigitalOcean DOCR: "registry.digitalocean.com/your-registry/fleet:v1.0.0" image_tag = "fleetdm/fleet:v4.90.0" + # image_registry_credentials = "your-username:your-token" # private Docker Hub + # image_deploy_on_push = true # DOCR only instance_size_slug = "apps-s-1vcpu-1gb" # $12/mo instance_count = 1 debug_logging = false diff --git a/digitalocean/variables.tf b/digitalocean/variables.tf index 398c1fd..1faa917 100644 --- a/digitalocean/variables.tf +++ b/digitalocean/variables.tf @@ -28,15 +28,27 @@ variable "vpc_config" { } variable "fleet_config" { - description = "Configuration for the Fleet application deployment on App Platform." + description = <<-EOT + Configuration for the Fleet application deployment on App Platform. + + image_tag accepts a full image reference: + - Docker Hub: "fleetdm/fleet:v4.90.0" (default, official image) + - Docker Hub custom: "your-org/your-image:v1.0.0" + - DOCR: "registry.digitalocean.com/your-registry/your-image:v1.0.0" + + image_registry_credentials: "username:token" for private Docker Hub repos. + image_deploy_on_push: auto-deploy when a new image is pushed (DOCR only). + EOT type = object({ - image_tag = string - instance_size_slug = string - instance_count = number - license_key = optional(string) - debug_logging = bool - exec_migration = bool - extra_env_vars = optional(map(string)) + image_tag = string + image_registry_credentials = optional(string) + image_deploy_on_push = optional(bool, false) + instance_size_slug = string + instance_count = number + license_key = optional(string) + debug_logging = bool + exec_migration = bool + extra_env_vars = optional(map(string)) }) default = { image_tag = "fleetdm/fleet:v4.90.0"