# The "trail" job runs Trail, a self-hosted web analytics platform that reads # reverse proxy access logs directly. No JavaScript tracking required. # # For more information on Trail, refer to: # # https://github.com/open-wander/trail variable "datacenters" { description = "List of datacenters to deploy to" type = list(string) default = ["dc1"] } variable "trail_version" { description = "Trail Docker image tag" type = string default = "latest" } variable "log_file" { description = "Path to access log inside the container" type = string default = "/logs/access.log" } variable "retention_days" { description = "Number of days to retain analytics data" type = number default = 90 } variable "log_format" { description = "Log format: auto, traefik, apache, or nginx" type = string default = "auto" } variable "host_log_path" { description = "Host path to the reverse proxy access log directory" type = string default = "/var/log/traefik" } variable "host_data_path" { description = "Host path for persistent Trail data (SQLite DB)" type = string default = "/opt/trail/data" } variable "htpasswd_file" { description = "Path to htpasswd file for basic auth (empty to disable)" type = string default = "" } variable "geoip_path" { description = "Path to GeoIP database inside the container (empty to disable)" type = string default = "" } variable "service_provider" { description = "Service discovery provider (nomad or consul)" type = string default = "nomad" } variable "traefik_host" { description = "Hostname for Traefik routing (empty to disable Traefik tags)" type = string default = "" } job "trail" { datacenters = var.datacenters type = "service" # The "update" block specifies the update strategy of task groups. The update # strategy is used to control things like rolling upgrades, canaries, and # blue/green deployments. # # For more information and examples on the "update" block, refer to: # # https://developer.hashicorp.com/nomad/docs/job-specification/update # update { max_parallel = 1 min_healthy_time = "10s" healthy_deadline = "3m" progress_deadline = "10m" auto_revert = false canary = 0 } # The migrate block specifies the group's strategy for migrating off of # draining nodes. # # For more information on the "migrate" block, refer to: # # https://developer.hashicorp.com/nomad/docs/job-specification/migrate # migrate { max_parallel = 1 health_check = "checks" min_healthy_time = "10s" healthy_deadline = "5m" } ui { description = "Trail - Self-hosted web analytics" link { label = "Trail on GitHub" url = "https://github.com/open-wander/trail" } } group "trail" { count = 1 network { port "http" { to = 8080 } } # The "service" block instructs Nomad to register this task as a service # in the service discovery engine, which is currently Nomad or Consul. # # For more information and examples on the "service" block, refer to: # # https://developer.hashicorp.com/nomad/docs/job-specification/service # service { name = "trail" port = "http" provider = var.service_provider tags = flatten([ ["trail", "analytics"], var.traefik_host != "" ? [ "traefik.enable=true", "traefik.http.routers.trail.rule=Host(`${var.traefik_host}`)", "traefik.http.routers.trail.entrypoints=https", "traefik.http.routers.trail.tls=true", ] : [], ]) check { name = "alive" type = "http" path = "/" interval = "15s" timeout = "3s" } } # The "restart" block configures a group's behavior on task failure. # # For more information and examples on the "restart" block, refer to: # # https://developer.hashicorp.com/nomad/docs/job-specification/restart # restart { attempts = 2 interval = "30m" delay = "15s" mode = "fail" } # Trail uses SQLite stored on a host volume, so pin the allocation to the # same node on updates to preserve the database. ephemeral_disk { sticky = true migrate = true size = 300 } task "trail" { driver = "docker" config { image = "ghcr.io/open-wander/trail:${var.trail_version}" ports = ["http"] auth_soft_fail = true volumes = flatten([ ["${var.host_log_path}:/logs:ro"], ["${var.host_data_path}:/data"], var.htpasswd_file != "" ? ["${var.htpasswd_file}:/etc/trail/.htpasswd:ro"] : [], ]) } env { TRAIL_LOG_FILE = var.log_file TRAIL_DB_PATH = "/data/trail.db" TRAIL_LISTEN = ":8080" TRAIL_RETENTION_DAYS = var.retention_days TRAIL_LOG_FORMAT = var.log_format } dynamic "template" { for_each = var.htpasswd_file != "" ? [1] : [] content { data = "TRAIL_HTPASSWD_FILE=/etc/trail/.htpasswd" destination = "local/htpasswd.env" env = true } } dynamic "template" { for_each = var.geoip_path != "" ? [1] : [] content { data = "TRAIL_GEOIP_PATH=${var.geoip_path}" destination = "local/geoip.env" env = true } } identity { env = true file = true } resources { cpu = 200 memory = 256 } } } }