A self-hosted equestrian management app for budget tracking, ride logging with gait detection, calendar planning, and weather-aware scheduling — built for riders who want full control of their data.
- Budget Tracking — Monthly budgets with category breakdowns, spending charts, and savings goals
- Income Management — Track income sources with recurring entry support and year-over-year comparison
- Ride Logging — Record rides with gait breakdowns (walk/trot/canter), duration, calories, and horse-specific stats
- Gait Detection — Automatic walk/trot/canter classification via accelerometer on Apple Watch
- Calendar & Events — Monthly calendar with color-coded event types, ride scheduling, and day detail views
- Weather Integration — Apple WeatherKit forecasts for ride planning with temperature, wind, and conditions
- Checklists — Daily/weekly barn checklists with iCloud Reminders sync
- Email Ingest — Forward receipts via email webhook to auto-create budget transactions
- Apple Watch App — Standalone watchOS companion with GPS tracking, heart rate, and offline sync
- Dark Mode — Full light/dark theme with system preference detection
- Self-Hosted — Docker Compose stack with PostgreSQL, zero external dependencies required
- Framework — Next.js 14 (App Router, standalone output)
- Database — PostgreSQL 16
- Auth — NextAuth.js 4 with JWT + bcrypt credentials
- Styling — Tailwind CSS with CSS custom properties for theming
- Charts — Recharts
- Tables — TanStack Table
- Watch — Native watchOS 10+ (Swift, CoreMotion, HealthKit, CoreLocation)
- Clone and enter the repo:
git clone https://github.com/zaheria985/barnbook.git
cd barnbook- Create your env file:
cp .env.example .env- Set at least:
DB_PASSWORD(choose any password for the database)NEXTAUTH_SECRET(runopenssl rand -base64 32to generate one)NEXTAUTH_URL(for local Docker usehttp://localhost:3500)
- Start the stack (app + PostgreSQL):
docker compose pull
docker compose up -d- Open
http://localhost:3500
Default login:
- Email:
rider@barnbook.local - Password:
barnbook123
On first startup the app container automatically:
- Waits for Postgres to be ready
- Applies the full schema if the database is empty
- Runs any pending migrations
- Seeds a default login account (disable with
SEED_DEFAULT_USER=0)
The default docker-compose.yml runs both the app and PostgreSQL:
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: barnbook
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: barnbook
volumes:
- barnbook_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U barnbook -d barnbook"]
interval: 5s
timeout: 3s
retries: 20
app:
image: ${APP_IMAGE:-ghcr.io/zaheria985/barnbook:latest}
build: .
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://barnbook:${DB_PASSWORD}@db:5432/barnbook
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
NEXTAUTH_URL: ${NEXTAUTH_URL}
BOOTSTRAP_SCHEMA: ${BOOTSTRAP_SCHEMA:-1}
SEED_DEFAULT_USER: ${SEED_DEFAULT_USER:-1}
ports:
- "${APP_PORT:-3500}:3500"
volumes:
barnbook_data:Run it:
docker compose pull
docker compose up -dIf you already have a PostgreSQL server, run just the app:
services:
app:
image: ${APP_IMAGE:-ghcr.io/zaheria985/barnbook:latest}
restart: unless-stopped
environment:
DATABASE_URL: postgresql://user:pass@your-db-host:5432/barnbook
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
NEXTAUTH_URL: ${NEXTAUTH_URL}
BOOTSTRAP_SCHEMA: "1"
SEED_DEFAULT_USER: "1"
ports:
- "3500:3500"Set DATABASE_URL to your external PostgreSQL connection string.
Paste this into the Unraid Docker Compose Manager stack editor:
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: barnbook
POSTGRES_PASSWORD: barnbook
POSTGRES_DB: barnbook
volumes:
- /mnt/user/appdata/barnbook/db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U barnbook -d barnbook"]
interval: 5s
timeout: 3s
retries: 20
app:
image: ghcr.io/zaheria985/barnbook:latest
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
DATABASE_URL: postgresql://barnbook:barnbook@db:5432/barnbook
NEXTAUTH_SECRET: change-me-to-a-random-string
NEXTAUTH_URL: http://YOUR_UNRAID_IP:3500
BOOTSTRAP_SCHEMA: "1"
SEED_DEFAULT_USER: "1"
ports:
- "3500:3500"Replace YOUR_UNRAID_IP with your server's IP address (e.g. 192.168.1.100).
- Image:
ghcr.io/zaheria985/barnbook latestis published automatically frommainvia.github/workflows/docker-publish.yml.- Every publish also includes a short SHA tag.
If you want to build locally instead of pulling the prebuilt image:
docker compose up --build -d- Install dependencies:
npm install- Copy environment config:
cp .env.example .env- Set required values in
.env:
DATABASE_URL(pointing to a running PostgreSQL instance)NEXTAUTH_SECRETNEXTAUTH_URL(usehttp://localhost:3100for dev)
- Apply database schema and migrations:
npm run db:migrate- Start development server:
npm run devVisit http://localhost:3100.
| Command | Description |
|---|---|
npm run dev |
Run local dev server (port 3100) |
npm run build |
Build production app |
npm run lint |
Run lint checks |
npm run db:migrate |
Apply schema and SQL migrations |
npm run db:seed |
Seed sample data |
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
DB_PASSWORD |
Yes (Docker) | PostgreSQL password for Docker Compose |
NEXTAUTH_SECRET |
Yes | Session encryption key (openssl rand -base64 32) |
NEXTAUTH_URL |
Yes | App base URL (e.g. http://localhost:3500) |
BOOTSTRAP_SCHEMA |
No | Auto-apply schema on first run (default: 1) |
SEED_DEFAULT_USER |
No | Seed default login on first run (default: 1) |
APP_PORT |
No | Host port mapping (default: 3500) |
WEATHERKIT_KEY_ID |
No | Apple WeatherKit key ID |
WEATHERKIT_TEAM_ID |
No | Apple Developer team ID |
WEATHERKIT_SERVICE_ID |
No | WeatherKit service identifier |
WEATHERKIT_PRIVATE_KEY |
No | WeatherKit private key (base64) |
EMAIL_INGEST_SECRET |
No | Shared secret for email webhook |
Optional integrations (iCloud, WeatherKit, email) work without configuration — features gracefully degrade when credentials are not set.
The watch/ directory contains a standalone watchOS 10+ app for ride tracking:
- Gait Detection — Accelerometer-based walk/trot/canter classification in real time
- GPS Tracking — Distance and route recording via CoreLocation
- Heart Rate — Live BPM monitoring through HealthKit workout sessions
- Offline Sync — Rides queue locally and sync to the web API when connectivity returns
- Calorie Estimates — Rider and horse calorie calculations matching the web app formulas
Open watch/Barnbook.xcodeproj in Xcode 15+ to build and deploy. Requires macOS 14+ and an Apple Developer account for device deployment.
- PostgreSQL 16 is required.
- Schema source is
db/schema.sql. - Migrations are tracked in
db/migrations/and applied bydb/migrate.js. - On first Docker startup,
db/bootstrap.jsauto-applies the schema and seeds.
The backup service in docker-compose.yml runs pg_dump daily into the
barnbook_backups volume, keeping the newest 14 dumps. To restore or inspect:
# list backups
docker compose exec backup ls -1 /backups
# copy one out
docker compose cp backup:/backups/barnbook-YYYY-MM-DD_HHMMSS.sql ./restore.sql
# restore into the db
docker compose exec -T db psql -U barnbook -d barnbook < ./restore.sqlTake a manual dump before running data-affecting migrations:
docker compose exec db pg_dump -U barnbook barnbook > barnbook-$(date +%F).sql
- Login loop or auth failures:
- Verify
NEXTAUTH_URLmatches the URL you open in the browser. - Ensure
NEXTAUTH_SECRETis set and stable across restarts.
- Verify
- Database connection errors:
- Confirm PostgreSQL is running and healthy.
- Verify
DATABASE_URLcredentials and host/port.
- Missing tables or columns:
- Run
npm run db:migrate(or restart the Docker container, which runs migrations automatically).
- Run
- Integrations not working:
- Check that the relevant environment variables are set (iCloud, WeatherKit, etc.).
- Features degrade gracefully — missing credentials won't break the app.



