Nomploy Nomad Packs

← All packs

cefiro v0.1.0

Apps

Cefiro — a self-hosted social recipe platform and hands-on cooking helper (an AGPL fork of Norish): recipe discovery, meal planning, shopping lists, and step-by-step cook mode. Deployed as an all-in-one host-networked Nomad job — the Cefiro app plus PostgreSQL, Redis, and the Obscura page-renderer, with persistent volumes.

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

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

5 tasks http 3021db 5432redis 6379obscura 9222 3 volumes image ghcr.io/pipozzz/cefiro:latest tracks :latest image bumped today
Variables 23
values.hcl

Save as values.hcl, edit, then run:

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

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

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

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

# The PostgreSQL image for the bundled database (Cefiro targets PostgreSQL 17).
postgres_image = "postgres:17-alpine"

# The Redis image for real-time events and job queues.
redis_image = "redis:8.4.0"

# The Obscura page-renderer image used for URL recipe imports.
obscura_image = "norishapp/obscura:0.2.0-norish.1"

# Host port for the Cefiro web app (PORT).
port = 3021

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

# Host port for the co-located Redis.
redis_port = 6379

# Host port for the Obscura CDP endpoint.
obscura_port = 9222

# Password for the Cefiro PostgreSQL user.
db_password = "cefiro"

# Encryption master key (MASTER_KEY). CHANGE THIS and keep it STABLE — it derives all encryption keys, so changing it later invalidates encrypted data. Generate with: openssl rand -base64 32.
master_key = "change-me-openssl-rand-base64-32"

# Public URL Cefiro is reachable at (AUTH_URL). Empty = http://localhost:<port>. Set this to your real host/domain.
base_url = ""

# Enable email/password auth so you can create the first admin (PASSWORD_AUTH_ENABLED). Configure OIDC/OAuth later in Settings.
password_auth_enabled = true

# Named volume for uploaded images/videos (/app/uploads). Not needed if you switch STORAGE_DRIVER to s3.
uploads_volume = "cefiro_uploads"

# Named volume for PostgreSQL data (/var/lib/postgresql/data). Holds all recipes.
db_data_volume = "cefiro_db_data"

# Named volume for Redis persistence (/data).
redis_data_volume = "cefiro_redis_data"

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

# Resources for the Cefiro app task.
resources = {
    cpu    = 1000
    memory = 1024
  }

# Resources for the PostgreSQL task.
postgres_resources = {
    cpu    = 300
    memory = 256
  }

# Resources for the Redis task.
redis_resources = {
    cpu    = 200
    memory = 128
  }

# Resources for the Obscura renderer (runs headless Chromium).
obscura_resources = {
    cpu    = 500
    memory = 512
  }
NameTypeDefaultDescription
job_name string
"cefiro"
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/cefiro:latest"
The Cefiro app image. Pin a version/sha in production.
postgres_image string
"postgres:17-alpine"
The PostgreSQL image for the bundled database (Cefiro targets PostgreSQL 17).
redis_image string
"redis:8.4.0"
The Redis image for real-time events and job queues.
obscura_image string
"norishapp/obscura:0.2.0-norish.1"
The Obscura page-renderer image used for URL recipe imports.
port number
3021
Host port for the Cefiro web app (PORT).
db_port number
5432
Host port for the co-located PostgreSQL.
redis_port number
6379
Host port for the co-located Redis.
obscura_port number
9222
Host port for the Obscura CDP endpoint.
db_password key string
"cefiro"
Password for the Cefiro PostgreSQL user.
master_key set me string
"change-me-openssl-rand-base64-32"
Encryption master key (MASTER_KEY). CHANGE THIS and keep it STABLE — it derives all encryption keys, so changing it later invalidates encrypted data. Generate with: openssl rand -base64 32.
base_url string
""
Public URL Cefiro is reachable at (AUTH_URL). Empty = http://localhost:<port>. Set this to your real host/domain.
password_auth_enabled key bool
true
Enable email/password auth so you can create the first admin (PASSWORD_AUTH_ENABLED). Configure OIDC/OAuth later in Settings.
uploads_volume string
"cefiro_uploads"
Named volume for uploaded images/videos (/app/uploads). Not needed if you switch STORAGE_DRIVER to s3.
db_data_volume string
"cefiro_db_data"
Named volume for PostgreSQL data (/var/lib/postgresql/data). Holds all recipes.
redis_data_volume string
"cefiro_redis_data"
Named volume for Redis persistence (/data).
constraints list
[]
Placement constraints. Pin to the node holding the volumes. On a nomploy cluster: attribute = "$${meta.nomploy_control_plane}", operator = "=", value = "true".
resources object
{
    cpu    = 1000
    memory = 1024
  }
Resources for the Cefiro app task.
postgres_resources object
{
    cpu    = 300
    memory = 256
  }
Resources for the PostgreSQL task.
redis_resources object
{
    cpu    = 200
    memory = 128
  }
Resources for the Redis task.
obscura_resources object
{
    cpu    = 500
    memory = 512
  }
Resources for the Obscura renderer (runs headless Chromium).
Back up this pack

This pack stores data in 3 Docker named volumes: cefiro_uploadscefiro_db_datacefiro_redis_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/cefiro_uploads/_data \
  /var/lib/docker/volumes/cefiro_db_data/_data \
  /var/lib/docker/volumes/cefiro_redis_data/_data

rclone (sync to S3/R2)

rclone sync /var/lib/docker/volumes/cefiro_uploads/_data backup:<bucket>/cefiro_uploads
rclone sync /var/lib/docker/volumes/cefiro_db_data/_data backup:<bucket>/cefiro_db_data
rclone sync /var/lib/docker/volumes/cefiro_redis_data/_data backup:<bucket>/cefiro_redis_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

cefiro

Cefiro — a self-hosted social recipe platform and hands-on cooking helper for home cooks. Discover recipes ("cook with what you have"), plan the week, generate a shopping list from the meal plan, and get guided step-by-step in cook mode (with per-step timers). An AGPL-3.0 fork of Norish.

All-in-one host-networked Nomad job: the Cefiro app + PostgreSQL 17 + Redis + Obscura (the page-renderer used for URL recipe imports), with persistent volumes.

Deploy

nomad-pack registry add nomploy https://github.com/Nomploy/nomad-packs
nomad-pack run cefiro --registry=nomploy

Configure

Variable Default Description
port 3021 Web app port (PORT).
base_url ""http://localhost:<port> AUTH_URL — set to your real domain.
master_key change-me-… MASTER_KEYchange it and keep it stable (openssl rand -base64 32); it derives all encryption keys.
password_auth_enabled true Allow email/password auth so you can create the first admin.
db_password cefiro PostgreSQL password.
db_port / redis_port / obscura_port 5432 / 6379 / 9222 Host ports for the bundled services.
uploads_volume cefiro_uploads /app/uploads — uploaded images/videos.
db_data_volume cefiro_db_data /var/lib/postgresql/data — all recipes.
redis_data_volume cefiro_redis_data /data.
image ghcr.io/pipozzz/cefiro:latest App image. Pin a version/sha in production.
resources / *_resources see variables.hcl Per-task resources.

The app runs its database migrations on boot. Keep MASTER_KEY stable — changing it invalidates previously encrypted data. To store media in S3/R2 instead of the uploads volume, add STORAGE_DRIVER=s3 plus the S3_* env vars to the cefiro task. Pin the job to the node holding the volumes with constraints, and front it with a reverse proxy for TLS.