Nomploy Nomad Packs

← All packs

starring v0.1.3

Other

Starring — a self-hosted app (GitHub stars/bookmarks manager) deployed as an all-in-one host-networked Nomad job: the Starring app plus a bundled PostgreSQL with a persistent volume. Set better_auth_url to your real domain and a stable better_auth_secret; serves plain HTTP, front it with a reverse proxy for TLS.

nomad-pack run starring --registry nomploy
…or one line (add registry + run)
curl -fsSL https://packs.nomploy.com/install.sh | sh -s -- starring

Needs nomad-pack on PATH. The script only adds the nomploy registry and runs this pack.

3 tasks http 4321db 5432 1 volume image ghcr.io/pipozzz/starring:latest tracks :latest image bumped today
Variables 20
values.hcl

Save as values.hcl, edit, then run:

nomad-pack run starring -f values.hcl --registry nomploy
# The name of the Nomad job.
job_name = "starring"

# The Nomad namespace to deploy into.
namespace = "default"

# The datacenters to deploy to.
datacenters = ["*"]

# The Starring app image. Pin a version/sha in production.
image = "ghcr.io/pipozzz/starring:latest"

# The PostgreSQL image for the bundled database (Starring targets PostgreSQL 17). Only used when bundled_db = true.
postgres_image = "postgres:17-alpine"

# Run a bundled PostgreSQL inside this job (true), or connect to an external / managed database via database_url (false). With false, no postgres/wait-db tasks and no db port are generated.
bundled_db = true

# External PostgreSQL connection string, used when bundled_db = false. e.g. postgres://user:pass@host:5432/starring.
database_url = ""

# Extra environment variables for the app task, as a name → value map. Use for SMTP (e.g. SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM) and any other app settings. Only add keys NOT already set by a dedicated variable above (duplicate env keys are an HCL error).
extra_env = {}

# Host port for the Starring web app (PORT).
port = 4321

# Host port for the co-located PostgreSQL.
db_port = 5432

# Password for the Starring PostgreSQL user. CHANGE THIS.
db_password = "starring"

# Public URL Starring is reachable at (BETTER_AUTH_URL). Empty = http://localhost:<port>. Set this to your real host/domain, e.g. https://app.example.com.
better_auth_url = ""

# Secret that signs auth sessions (BETTER_AUTH_SECRET). CHANGE THIS and keep it STABLE. Generate with: openssl rand -hex 32.
better_auth_secret = "change-me-openssl-rand-hex-32"

# Optional public site URL (SITE_URL). Empty = unset.
site_url = ""

# Allow open sign-ups (ALLOW_SIGNUP). Leave false and sign in / invite instead.
allow_signup = false

# Enable email/password auth (AUTH_PASSWORD) so you can create the first user.
password_auth = true

# Named volume for PostgreSQL data (/var/lib/postgresql/data). Holds all data — back it up.
db_data_volume = "starring_db_data"

# Placement constraints. Pin to the node holding the volume. On a nomploy cluster: attribute = "$${meta.nomploy_control_plane}", operator = "=", value = "true".
constraints = []

# Resources for the Starring app task.
resources = {
    cpu    = 500
    memory = 512
  }

# Resources for the PostgreSQL task.
postgres_resources = {
    cpu    = 300
    memory = 256
  }
NameTypeDefaultDescription
job_name string
"starring"
The name of the Nomad job.
namespace string
"default"
The Nomad namespace to deploy into.
datacenters list
["*"]
The datacenters to deploy to.
image string
"ghcr.io/pipozzz/starring:latest"
The Starring app image. Pin a version/sha in production.
postgres_image string
"postgres:17-alpine"
The PostgreSQL image for the bundled database (Starring targets PostgreSQL 17). Only used when bundled_db = true.
bundled_db bool
true
Run a bundled PostgreSQL inside this job (true), or connect to an external / managed database via database_url (false). With false, no postgres/wait-db tasks and no db port are generated.
database_url string
""
External PostgreSQL connection string, used when bundled_db = false. e.g. postgres://user:pass@host:5432/starring.
extra_env map
{}
Extra environment variables for the app task, as a name → value map. Use for SMTP (e.g. SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM) and any other app settings. Only add keys NOT already set by a dedicated variable above (duplicate env keys are an HCL error).
port number
4321
Host port for the Starring web app (PORT).
db_port number
5432
Host port for the co-located PostgreSQL.
db_password key string
"starring"
Password for the Starring PostgreSQL user. CHANGE THIS.
better_auth_url string
""
Public URL Starring is reachable at (BETTER_AUTH_URL). Empty = http://localhost:<port>. Set this to your real host/domain, e.g. https://app.example.com.
better_auth_secret set me string
"change-me-openssl-rand-hex-32"
Secret that signs auth sessions (BETTER_AUTH_SECRET). CHANGE THIS and keep it STABLE. Generate with: openssl rand -hex 32.
site_url string
""
Optional public site URL (SITE_URL). Empty = unset.
allow_signup bool
false
Allow open sign-ups (ALLOW_SIGNUP). Leave false and sign in / invite instead.
password_auth key bool
true
Enable email/password auth (AUTH_PASSWORD) so you can create the first user.
db_data_volume string
"starring_db_data"
Named volume for PostgreSQL data (/var/lib/postgresql/data). Holds all data — back it up.
constraints list
[]
Placement constraints. Pin to the node holding the volume. On a nomploy cluster: attribute = "$${meta.nomploy_control_plane}", operator = "=", value = "true".
resources object
{
    cpu    = 500
    memory = 512
  }
Resources for the Starring app task.
postgres_resources object
{
    cpu    = 300
    memory = 256
  }
Resources for the PostgreSQL task.
Back up this pack

This pack stores data in one Docker named volume: starring_db_data

⚠ This pack bundles a database. A cold copy of the volume can be inconsistent — for a reliable backup, dump the DB (pg_dump / mysqldump) or stop the job while backing up.

restic

# Run on the node hosting this pack. Point restic at your repo first:
#   export RESTIC_REPOSITORY="s3:https://<account>.r2.cloudflarestorage.com/<bucket>"
#   export RESTIC_PASSWORD="<repo-password>"
#   export AWS_ACCESS_KEY_ID=<key>  AWS_SECRET_ACCESS_KEY=<secret>
restic backup \
  /var/lib/docker/volumes/starring_db_data/_data

rclone (sync to S3/R2)

rclone sync /var/lib/docker/volumes/starring_db_data/_data backup:<bucket>/starring_db_data

Paths assume the default Docker volume location (/var/lib/docker/volumes). Restore by stopping the job, restoring files into the same volume, and re-running the pack.

Readme

Starring

Starring deployed as an all-in-one, host-networked Nomad job: the Starring app plus a bundled PostgreSQL 17 with a persistent volume. The app task is gated on the database accepting connections (a prestart wait task), so the first migration doesn't race a cold database.

Variables

Variable Default Description
image ghcr.io/pipozzz/starring:latest App image (pin a version in production).
postgres_image postgres:17-alpine Bundled PostgreSQL image (when bundled_db).
bundled_db true Run a bundled PostgreSQL. Set false to use an external / managed DB via database_url.
database_url "" External PostgreSQL connection string (when bundled_db = false).
port 4321 Host port for the web app.
db_port 5432 Host port for the co-located PostgreSQL.
db_password starring Change this. PostgreSQL password.
better_auth_url "" Public URL (BETTER_AUTH_URL). Empty → http://localhost:<port>. Set to your real domain.
better_auth_secret change-me-… Change this, keep it stable. Signs auth sessions. openssl rand -hex 32.
site_url "" Optional SITE_URL.
allow_signup false Open sign-ups.
password_auth true Email/password auth (create the first user).
db_data_volume starring_db_data Named volume for PostgreSQL data — back it up.
constraints [] Pin to the node holding the volume (see below).
resources / postgres_resources — Per-task CPU/memory.

Persistence & placement

All data lives in the db_data_volume docker named volume, which is node-local. Pin the job to the node that holds it so a reschedule always finds the data. On a nomploy cluster:

constraints = [{
  attribute = "${meta.nomploy_control_plane}"
  operator  = "="
  value     = "true"
}]

Serves plain HTTP — front it with Traefik / a reverse proxy for TLS, and set better_auth_url to that public HTTPS URL.

Example

nomad-pack run starring \
  --var better_auth_url=https://app.example.com \
  --var better_auth_secret=$(openssl rand -hex 32) \
  --var db_password=$(openssl rand -hex 16)