job "ntfy" { group "ntfy" { network { mode = "bridge" port "http" { to = 80 } } # Consul Connect sidecar with upstream to postgres service { connect { sidecar_service { proxy { upstreams { destination_name = "postgres" local_bind_port = 5432 } } } } } service { name = "ntfy" port = "http" tags = [ "traefik.enable=true", "traefik.http.routers.ntfy.middlewares=auth@file", # Token subdomain bypasses Authelia — ntfy's own token auth is sufficient for API access "traefik.http.routers.ntfy-token.rule=Host(`ntfy-2e30e5869ab6bfde4961012b48761a9b.othrayte.one`)", ] check { type = "http" path = "/healthz" interval = "10s" timeout = "2s" } } # Users and tokens are provisioned declaratively via auth-users / auth-tokens in server.yml. # ntfy reads and applies them on every startup — no poststart task, no race conditions. # # Bcrypt hashes are not secrets and are hardcoded below (same as /etc/shadow — safe to commit). # Generate with: docker run --rm -it binwiederhier/ntfy user hash # or: echo "mypassword" | docker run --rm -i binwiederhier/ntfy user hash # For the diun account the plaintext is irrelevant (token-only auth); use a random password: # openssl rand -base64 32 | docker run --rm -i binwiederhier/ntfy user hash # # Required SOPS keys: # ntfy.database_pw — postgres password for the ntfy role # diun.ntfy_token — access token for Diun (actual secret — grants write access) # Must start with "tk_" and be exactly 32 chars total. # Generate: tok=$(openssl rand -hex 15); echo "tk_${tok:0:29}" task "ntfy" { driver = "docker" config { image = "binwiederhier/ntfy:latest" ports = ["http"] command = "serve" volumes = [ "local/server.yml:/etc/ntfy/server.yml", ] } env = { TZ = "Australia/Melbourne" } template { data = <