Initial setup: OpenResto deployment for Lunkaberg breakfast booking

Adapts upstream's release docker-compose.yml to run behind this server's
Nginx Proxy Manager on the shared my-main-net network instead of publishing
host ports. Includes docs/ for session continuity.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014vNjtg18oTQcv8dtjwnT69
This commit is contained in:
root
2026-09-21 17:27:38 +02:00
co-authored by Claude Sonnet 5
commit 42b1060c19
6 changed files with 229 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
# Copy to .env and fill in real values. Never commit the real .env.
# --- Required ---
# JWT signing key, min 32 chars: openssl rand -base64 48
JWT_KEY=
# Public URL this deployment is served on
CORS_ORIGINS=https://frukost.lunkaberg.se
# Seeds the admin account on first boot only
ADMIN_EMAIL=
ADMIN_PASSWORD=
# --- Optional: SMTP for booking confirmation emails ---
SMTP_HOST=
SMTP_PORT=587
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_EMAIL=
SMTP_FROM_NAME=Lunkaberg Frukost
SMTP_ENABLE_SSL=true
# --- Optional: push notifications ---
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=
VAPID_SUBJECT=
# --- Optional: Apple/Google Wallet passes ---
APPLE_PASS_TYPE_ID=
APPLE_TEAM_ID=
APPLE_PASS_CERTIFICATE_PATH=
APPLE_PASS_CERTIFICATE_PASSWORD=
APPLE_WWDR_CERTIFICATE_PATH=
GOOGLE_WALLET_ISSUER_ID=
GOOGLE_WALLET_SERVICE_ACCOUNT_KEY_PATH=
+3
View File
@@ -0,0 +1,3 @@
.env
wallet/
well-known/
+100
View File
@@ -0,0 +1,100 @@
# OpenResto — self-hosted breakfast booking (Lunkaberg)
#
# Adapted from the official release compose (v2.2.0) to run behind the
# existing Nginx Proxy Manager setup on this server: no host ports are
# published, all services join the shared `my-main-net` network, and NPM
# proxies straight to the `lunkaberg-reverse-proxy` container on port 80.
#
# Backup: see https://github.com/karanshukla/openresto/blob/main/docs/backup-restore.md
volumes:
db_data:
media_data:
networks:
main:
external: true
name: my-main-net
services:
backend:
image: ghcr.io/karanshukla/openresto-backend:2.2.0
container_name: lunkaberg-backend
restart: unless-stopped
networks:
- main
environment:
- ASPNETCORE_ENVIRONMENT=Production
- PORT=8080
- CORS_ORIGINS=${CORS_ORIGINS}
- JWT_KEY=${JWT_KEY}
- ADMIN_EMAIL=${ADMIN_EMAIL}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- CONNECTION_STRING=Data Source=/data/openresto.db
- DATA_PROTECTION_KEYS_PATH=/data/dp-keys
- Vapid__PublicKey=${VAPID_PUBLIC_KEY:-}
- Vapid__PrivateKey=${VAPID_PRIVATE_KEY:-}
- Vapid__Subject=${VAPID_SUBJECT:-}
- GuestPush__ReminderLeadHours=${GUEST_PUSH_REMINDER_LEAD_HOURS:-24,2}
- GuestPush__ExpoAccessToken=${EXPO_ACCESS_TOKEN:-}
- Wallet__Apple__PassTypeIdentifier=${APPLE_PASS_TYPE_ID:-}
- Wallet__Apple__TeamIdentifier=${APPLE_TEAM_ID:-}
- Wallet__Apple__CertificatePath=${APPLE_PASS_CERTIFICATE_PATH:-}
- Wallet__Apple__CertificatePassword=${APPLE_PASS_CERTIFICATE_PASSWORD:-}
- Wallet__Apple__WwdrCertificatePath=${APPLE_WWDR_CERTIFICATE_PATH:-}
- Wallet__Google__IssuerId=${GOOGLE_WALLET_ISSUER_ID:-}
- Wallet__Google__ServiceAccountKeyPath=${GOOGLE_WALLET_SERVICE_ACCOUNT_KEY_PATH:-}
- EmailSettings__Host=${SMTP_HOST:-}
- EmailSettings__Port=${SMTP_PORT:-587}
- EmailSettings__Username=${SMTP_USERNAME:-}
- EmailSettings__Password=${SMTP_PASSWORD:-}
- EmailSettings__FromEmail=${SMTP_FROM_EMAIL:-}
- EmailSettings__FromName=${SMTP_FROM_NAME:-Lunkaberg Frukost}
- EmailSettings__EnableSsl=${SMTP_ENABLE_SSL:-true}
volumes:
- db_data:/data
- media_data:/app/wwwroot/media
- ./wallet:/wallet:ro
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
frontend:
image: ghcr.io/karanshukla/openresto-frontend:2.2.0
container_name: lunkaberg-frontend
restart: unless-stopped
networks:
- main
environment:
- EXPO_PUBLIC_API_URL=/api
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8081"]
interval: 10s
timeout: 5s
retries: 3
reverse-proxy:
image: ghcr.io/karanshukla/openresto-nginx:2.2.0
container_name: lunkaberg-reverse-proxy
restart: unless-stopped
networks:
- main
# No host port published — Nginx Proxy Manager reaches this container
# directly on my-main-net at lunkaberg-reverse-proxy:80.
environment:
- PORT=80
- BACKEND_HOST=backend
- BACKEND_PORT=8080
- FRONTEND_HOST=frontend
- FRONTEND_PORT=8081
volumes:
- media_data:/usr/share/nginx/html/media:ro
- ./well-known:/usr/share/nginx/html/.well-known:ro
depends_on:
backend:
condition: service_healthy
frontend:
condition: service_healthy
+28
View File
@@ -0,0 +1,28 @@
# Decisions — Lunkaberg Frukost (OpenResto)
## 2026-09-21 — Use OpenResto rather than build custom
Per wanted to evaluate get.openres.to for a client breakfast-booking site.
It's MIT-licensed, self-hosted, ASP.NET Core + React + SQLite, ships
pre-built multi-arch Docker images and is explicitly designed to sit behind
a reverse proxy like NPM. Decided to deploy it as-is rather than build a
custom PHP tool, since it already covers table holds, multi-location,
service periods, and an admin dashboard out of the box.
## 2026-09-21 — Git via self-hosted Gitea, not the usual live-edit workflow
Per's standing default for hobby projects is no git / live-editing on the
server. This is client work, so he's using his Gitea instance
(gitea.axbom.com) instead — repo `root/LunkabergB`. `.env` (secrets) is
gitignored; `.env.example` documents the required variables.
## 2026-09-21 — No host ports published for the OpenResto containers
Upstream's docker-compose.yml publishes the bundled nginx reverse-proxy
container on host port 80 by default, which would collide with this
server's Nginx Proxy Manager (already bound to 80/443). Adapted the compose
file to join the existing `my-main-net` Docker network instead (same
pattern as vox, remark42, etc.) — NPM proxies straight to
`lunkaberg-reverse-proxy:80` over the Docker network, no host port needed.
## 2026-09-21 — Admin email defaulted to per@axbom.se
Used as a placeholder so there's a working login immediately; this should
be revisited once it's clear who administers bookings day-to-day for the
client (Per vs. someone at Lunkaberg).
+39
View File
@@ -0,0 +1,39 @@
# State — Lunkaberg Frukost (OpenResto)
## What this is
Client project for lunkaberg.se — a breakfast booking service, built on
[OpenResto](https://get.openres.to) (open source, MIT), self-hosted at
`frukost.lunkaberg.se`.
## Stack
- OpenResto v2.2.0, official pre-built images (`ghcr.io/karanshukla/openresto-*`)
- Backend: ASP.NET Core 10 + SQLite (in `db_data` volume)
- Frontend: React (Expo/React Native web build)
- Reverse proxy: bundled nginx container (`lunkaberg-reverse-proxy`), fronted
by this server's Nginx Proxy Manager — no host ports published, everything
rides the shared `my-main-net` Docker network like the other app projects
on this box.
- Location: `/var/www/lunkaberg/`
- Git: tracked, remote at `https://gitea.axbom.com/root/LunkabergB.git`
(this is a client project, so — unlike most of Per's hobby projects —
it's using real version control instead of live-editing.)
## Status as of 2026-09-21
- [x] Project directory created, git initialized, Gitea remote added
- [x] docker-compose.yml adapted from upstream release for the my-main-net /
NPM pattern (no host port conflicts with NPM's own 80/443)
- [x] .env created with generated JWT_KEY and admin password (not committed —
see .env.example for the template)
- [ ] `docker compose up -d` — pulling images / bringing the stack up
- [ ] DNS: A record for frukost.lunkaberg.se → 64.112.127.212 (Per manages
lunkaberg.se DNS himself)
- [ ] NPM proxy host for frukost.lunkaberg.se → lunkaberg-reverse-proxy:80
- [ ] First login / admin dashboard walkthrough
- [ ] Decide: SMTP for booking confirmation emails (needs a mailbox at
lunkaberg.se, separate from Per's personal Migadu setup)
- [ ] Decide: push notifications (VAPID), Apple/Google Wallet passes —
left disabled for now, app degrades gracefully without them
## Known issues / watch items
- Server disk was at 87% (6.5GB free) before pulling OpenResto images —
keep an eye on `df -h` after this and future installs.
+24
View File
@@ -0,0 +1,24 @@
# TODO — Lunkaberg Frukost (OpenResto)
## Next up
1. Bring the stack up (`docker compose up -d`) and confirm all three
containers report healthy.
2. Per: add DNS A record for `frukost.lunkaberg.se``64.112.127.212`.
3. Create NPM proxy host: `frukost.lunkaberg.se``lunkaberg-reverse-proxy:80`,
request Let's Encrypt cert, force SSL.
4. Log into the admin dashboard with the generated credentials (see .env),
change the admin password from the generated one via the UI if it
offers that, and set up the restaurant's first location (timezone,
hours, tables, floor sections).
5. Push the initial commit to Gitea once Per confirms he's ready.
## Open questions for Per
- Who is the actual admin user for the client (Per, or someone at
Lunkaberg)? Affects ADMIN_EMAIL and whether Per hands off credentials.
- Want booking confirmation emails? Needs SMTP creds — likely a new mailbox
at lunkaberg.se via Migadu, separate from Per's personal one.
- Any interest in push notifications or Apple/Google Wallet passes, or
leave those disabled (current default)?
- Branding: restaurant name / primary color / icon are configurable live
from the admin dashboard, no redeploy needed — revisit once client has
a look.