job "paperless" { datacenters = ["dc1"] region = "global" group "paperless" { network { mode = "bridge" } volume "data" { type = "csi" source = "paperless-data" access_mode = "single-node-writer" attachment_mode = "file-system" } volume "input" { type = "csi" source = "paperless-input" access_mode = "single-node-writer" attachment_mode = "file-system" } service { name = "paperless" port = 8994 connect { sidecar_service { proxy { upstreams { destination_name = "postgres" local_bind_port = 5432 # Work arround, see https://github.com/hashicorp/nomad/issues/18538 destination_type = "service" } } } sidecar_task { config { args = [ "-c", "${NOMAD_SECRETS_DIR}/envoy_bootstrap.json", "-l", "${meta.connect.log_level}", "--concurrency", "${meta.connect.proxy_concurrency}", "--disable-hot-restart" ] } resources { cpu = 50 memory = 64 } } } tags = [ "traefik.enable=true", "traefik.http.routers.paperless.entrypoints=https", "traefik.http.routers.paperless.rule=Host(`paperless.example.org`)", "traefik.http.middlewares.csp-paperless.headers.contentsecuritypolicy=default-src 'self';font-src 'self' data:;img-src 'self' data:;script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline';", "traefik.http.routers.paperless.middlewares=security-headers@file,rate-limit-std@file,forward-proto@file,inflight-std@file,hsts@file,compression@file,csp-paperless", ] } # wait for required services tp be ready before starting the main task task "wait-for" { driver = "docker" user = 1053 config { image = "danielberteaud/wait-for:24.3-1" readonly_rootfs = true pids_limit = 20 } lifecycle { hook = "prestart" } env { SERVICE_0 = "master.postgres.service.consul" } resources { cpu = 10 memory = 10 memory_max = 30 } } # Local redis instance task "redis" { driver = "docker" user = 6379 lifecycle { hook = "prestart" sidecar = true } config { image = "redis:alpine" readonly_rootfs = true force_pull = true args = ["/local/redis.conf"] } template { data = <<_EOT bind 127.0.0.1 maxmemory {{ env "NOMAD_MEMORY_LIMIT" | parseInt | subtract 5 }}mb databases 1 save "" appendonly no _EOT destination = "local/redis.conf" } resources { cpu = 10 memory = 20 } } task "webserver" { driver = "docker" config { image = "danielberteaud/paperless-ngx:2.6.3-1" image_pull_timeout = "10m" readonly_rootfs = true pids_limit = 200 } vault { policies = ["paperless"] env = false disable_file = true change_mode = "noop" } env { PAPERLESS_MODE = "webserver" PAPERLESS_BIND_ADDR = "127.0.0.1" PAPERLESS_TRUSTED_PROXIES = "127.0.0.1" PAPERLESS_PROXY_SSL_HEADER = "[\"HTTP_X_FORWARDED_PROTO\", \"https\"]" PAPERLESS_CONVERT_TMPDIR = "/alloc/data" PAPERLESS_USE_X_FORWARD_HOST = "true" PAPERLESS_URL = "https://paperless.example.org" PAPERLESS_ENABLE_COMPRESSION = false TMPDIR = "/alloc/tmp" } # Use a template block instead of env {} so we can fetch values from vault template { data = <<_EOT LANG=fr_FR.utf8 PAPERLESS_CORS_ALLOWED_HOSTS=https://paperless.example.org PAPERLESS_SECRET_KEY={{ with secret "kv/service/paperless" }}{{ .Data.data.secret_key }}{{ end }} TZ=Europe/Paris _EOT destination = "secrets/.env" perms = 400 env = true } template { data = <<_EOT PAPERLESS_DBHOST=127.0.0.1 PAPERLESS_DBPORT=5432 PAPERLESS_DBUSER={{ with secret "database/creds/paperless" }}{{ .Data.username }}{{ end }} PAPERLESS_DBPASS={{ with secret "database/creds/paperless" }}{{ .Data.password }}{{ end }} _EOT destination = "secrets/.db.env" perms = 400 env = true } volume_mount { volume = "data" destination = "/data" } volume_mount { volume = "input" destination = "/input" } resources { cpu = 300 memory = 512 } } task "consumer" { driver = "docker" lifecycle { hook = "poststart" sidecar = true } config { image = "danielberteaud/paperless-ngx:2.6.3-1" image_pull_timeout = "10m" readonly_rootfs = true pids_limit = 100 } vault { policies = ["paperless"] env = false disable_file = true change_mode = "noop" } env { PAPERLESS_MODE = "consumer" PAPERLESS_CONVERT_TMPDIR = "/alloc/data" TMPDIR = "/alloc/tmp" } # Use a template block instead of env {} so we can fetch values from vault template { data = <<_EOT LANG=fr_FR.utf8 PAPERLESS_CORS_ALLOWED_HOSTS=https://paperless.example.org PAPERLESS_SECRET_KEY={{ with secret "kv/service/paperless" }}{{ .Data.data.secret_key }}{{ end }} TZ=Europe/Paris _EOT destination = "secrets/.env" perms = 400 env = true } template { data = <<_EOT PAPERLESS_DBHOST=127.0.0.1 PAPERLESS_DBPORT=5432 PAPERLESS_DBUSER={{ with secret "database/creds/paperless" }}{{ .Data.username }}{{ end }} PAPERLESS_DBPASS={{ with secret "database/creds/paperless" }}{{ .Data.password }}{{ end }} _EOT destination = "secrets/.db.env" perms = 400 env = true } volume_mount { volume = "data" destination = "/data" } volume_mount { volume = "input" destination = "/input" } resources { cpu = 100 memory = 256 } } task "scheduler" { driver = "docker" lifecycle { hook = "prestart" sidecar = true } config { image = "danielberteaud/paperless-ngx:2.6.3-1" image_pull_timeout = "10m" readonly_rootfs = true pids_limit = 100 } vault { policies = ["paperless"] env = false disable_file = true change_mode = "noop" } env { PAPERLESS_MODE = "scheduler" PAPERLESS_CONVERT_TMPDIR = "/alloc/data" TMPDIR = "/alloc/tmp" } # Use a template block instead of env {} so we can fetch values from vault template { data = <<_EOT LANG=fr_FR.utf8 PAPERLESS_CORS_ALLOWED_HOSTS=https://paperless.example.org PAPERLESS_SECRET_KEY={{ with secret "kv/service/paperless" }}{{ .Data.data.secret_key }}{{ end }} TZ=Europe/Paris _EOT destination = "secrets/.env" perms = 400 env = true } template { data = <<_EOT PAPERLESS_DBHOST=127.0.0.1 PAPERLESS_DBPORT=5432 PAPERLESS_DBUSER={{ with secret "database/creds/paperless" }}{{ .Data.username }}{{ end }} PAPERLESS_DBPASS={{ with secret "database/creds/paperless" }}{{ .Data.password }}{{ end }} _EOT destination = "secrets/.db.env" perms = 400 env = true } volume_mount { volume = "data" destination = "/data" } volume_mount { volume = "input" destination = "/input" } resources { cpu = 100 memory = 200 } } task "task-queue" { driver = "docker" lifecycle { hook = "poststart" sidecar = true } config { image = "danielberteaud/paperless-ngx:2.6.3-1" image_pull_timeout = "10m" readonly_rootfs = true pids_limit = 300 } vault { policies = ["paperless"] env = false disable_file = true change_mode = "noop" } env { PAPERLESS_MODE = "task-queue" PAPERLESS_CONVERT_TMPDIR = "/alloc/data" TMPDIR = "/alloc/tmp" } # Use a template block instead of env {} so we can fetch values from vault template { data = <<_EOT LANG=fr_FR.utf8 PAPERLESS_CORS_ALLOWED_HOSTS=https://paperless.example.org PAPERLESS_SECRET_KEY={{ with secret "kv/service/paperless" }}{{ .Data.data.secret_key }}{{ end }} TZ=Europe/Paris _EOT destination = "secrets/.env" perms = 400 env = true } template { data = <<_EOT PAPERLESS_DBHOST=127.0.0.1 PAPERLESS_DBPORT=5432 PAPERLESS_DBUSER={{ with secret "database/creds/paperless" }}{{ .Data.username }}{{ end }} PAPERLESS_DBPASS={{ with secret "database/creds/paperless" }}{{ .Data.password }}{{ end }} _EOT destination = "secrets/.db.env" perms = 400 env = true } volume_mount { volume = "data" destination = "/data" } volume_mount { volume = "input" destination = "/input" } resources { cpu = 500 memory = 384 memory_max = 512 } } } }