compose.yaml
Rama main · réplica de lectura del repositorio original.
name: sparkyfitness
x-daemonless:
class: "service"
title: "SparkyFitness"
icon: ":material-heart-pulse:"
category: "Productivity"
description: "Self-hosted privacy-first fitness tracker on FreeBSD."
upstream_url: "https://github.com/CodeWithCJ/SparkyFitness"
web_url: "https://github.com/CodeWithCJ/SparkyFitness"
user: "bsd"
mlock: false
# sparkyfitness is the one people open; its database stays on the stack's
# private network, where it is found by the name fjord puts in
# SPARKY_FITNESS_DB_HOST (127.0.0.1 on host networking).
networking:
sparkyfitness: default
"*": private
hostnames:
sparkyfitness-db: SPARKY_FITNESS_DB_HOST
# Sidecar for the AppJail director bundle, named as the service is so fjord
# finds its jail by the service's name.
appjail:
director:
options:
- alias:
- ip4_inherit:
depends_on:
- name: sparkyfitness-db
image: ghcr.io/daemonless/postgres:18
template: postgres-template.conf
env:
POSTGRES_DB: "!ENV '${SPARKY_FITNESS_DB_NAME}'"
POSTGRES_USER: "!ENV '${SPARKY_FITNESS_DB_USER}'"
POSTGRES_PASSWORD: "!ENV '${SPARKY_FITNESS_DB_PASSWORD}'"
POSTGRES_PORT: "5433"
volumes:
db_data: /var/lib/postgresql/data
docs:
env:
PUID: "User ID for the application process"
PGID: "Group ID for the application process"
TZ: "Timezone for the container"
UPLOADS_LOCATION: "Path to store user uploads (profile pictures, exercise images)"
BACKUPS_LOCATION: "Path to store server backups"
DB_DATA_LOCATION: "Path to store PostgreSQL data files"
SPARKY_FITNESS_DB_NAME: "PostgreSQL database name"
SPARKY_FITNESS_DB_USER: "PostgreSQL superuser"
SPARKY_FITNESS_DB_PASSWORD: "PostgreSQL password"
SPARKY_FITNESS_API_ENCRYPTION_KEY: "64-char hex encryption key"
BETTER_AUTH_SECRET: "Auth session signing secret"
SPARKY_FITNESS_FRONTEND_URL:
desc: "URL you will open this app at, e.g. http://myhost:3004 (sign-up fails if it does not match)"
public_url: true # fjord fills in where the app lands
NODE_ENV: "Node runtime mode; leave as 'production'"
SPARKY_FITNESS_DB_HOST:
desc: "Where the backend finds PostgreSQL (fjord sets this; 127.0.0.1 on host networking)"
level: advanced
optional: true
SPARKY_FITNESS_DB_PORT: "PostgreSQL port; MUST match the sidecar's POSTGRES_PORT. Default 5433 (NOT 5432) so this can coexist with another host-networked Postgres (e.g. Immich on 5432) on the same host. ⚠ With network_mode: host, two Postgres on the same port silently collide — keep each service on a distinct port. NOTE: this only takes effect if the daemonless/postgres image honors POSTGRES_PORT (see project README / upstream fix); otherwise the sidecar falls back to 5432."
SPARKY_FITNESS_APP_DB_USER: "Limited app DB role the backend creates during migrations"
SPARKY_FITNESS_APP_DB_PASSWORD: "Password for the limited app DB role"
SPARKY_FITNESS_SERVER_HOST: "Internal bind address for the node backend; leave as 127.0.0.1 (nginx proxies to it)"
SPARKY_FITNESS_SERVER_PORT: "Internal node backend port; leave as default"
SPARKY_FITNESS_LOG_LEVEL: "Backend log verbosity (e.g. ERROR, INFO, DEBUG)"
volumes:
/app/server/uploads: "User-uploaded files (profile pictures, exercise images)"
/app/server/backup: "Server backup data"
ports:
3004: "Web UI (nginx reverse proxy)"
services:
sparkyfitness:
# Tag in a ${VAR} so the catalog exposes it as an image_tag variable and the
# install wizard can offer this repo's published versions. The database is
# pinned deliberately: retagging it would be a destructive migration.
image: ghcr.io/daemonless/sparkyfitness:${SPARKYFITNESS_TAG:-latest}
container_name: sparkyfitness
restart: unless-stopped
network_mode: host
depends_on:
- sparkyfitness-db
environment:
- PUID=1000
- PGID=1000
- TZ=${TZ:-UTC}
- NODE_ENV=production
- SPARKY_FITNESS_DB_HOST=${SPARKY_FITNESS_DB_HOST:-127.0.0.1}
- SPARKY_FITNESS_DB_PORT=5433
- SPARKY_FITNESS_DB_NAME=${SPARKY_FITNESS_DB_NAME}
- SPARKY_FITNESS_DB_USER=${SPARKY_FITNESS_DB_USER}
- SPARKY_FITNESS_DB_PASSWORD=${SPARKY_FITNESS_DB_PASSWORD}
- SPARKY_FITNESS_APP_DB_USER=${SPARKY_FITNESS_APP_DB_USER}
- SPARKY_FITNESS_APP_DB_PASSWORD=${SPARKY_FITNESS_APP_DB_PASSWORD}
- SPARKY_FITNESS_API_ENCRYPTION_KEY=${SPARKY_FITNESS_API_ENCRYPTION_KEY}
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
- SPARKY_FITNESS_FRONTEND_URL=${SPARKY_FITNESS_FRONTEND_URL}
- SPARKY_FITNESS_SERVER_HOST=127.0.0.1
- SPARKY_FITNESS_SERVER_PORT=3010
- SPARKY_FITNESS_LOG_LEVEL=ERROR
volumes:
- ${UPLOADS_LOCATION}:/app/server/uploads
- ${BACKUPS_LOCATION}:/app/server/backup
sparkyfitness-db:
image: ghcr.io/daemonless/postgres:18
container_name: sparkyfitness-db
restart: unless-stopped
network_mode: host
annotations:
org.freebsd.jail.allow.sysvipc: "true"
# Generic daemonless PostgreSQL 18 image. Only the superuser role + database
# are provisioned here; the SparkyFitness backend creates its own limited
# app role (SPARKY_FITNESS_APP_DB_USER) during migrations.
environment:
- POSTGRES_DB=${SPARKY_FITNESS_DB_NAME}
- POSTGRES_USER=${SPARKY_FITNESS_DB_USER}
- POSTGRES_PASSWORD=${SPARKY_FITNESS_DB_PASSWORD}
# Distinct port (NOT 5432) so this can run alongside another host-networked Postgres
# (e.g. Immich on 5432) without colliding. ⚠ Requires the daemonless/postgres image to
# honor POSTGRES_PORT — if it doesn't, it falls back to 5432 and WILL collide. Two
# host-net Postgres must never share a port.
- POSTGRES_PORT=5433
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data