FamilyNido — a self-hosted PWA for a single household: shared calendar, chores, meals, school agenda, health records and a family wall. One instance per family, deployable with `docker compose` on any home server. Stack: .NET 10 (ASP.NET Core Minimal APIs) + EF Core 10 + PostgreSQL 16 on the backend, Angular 21 (standalone, signals, zoneless) + Tailwind CSS v4 on the frontend, SignalR for realtime, optional OIDC alongside local credentials, integration via a versioned `/api/v1/**` public API. See README.md for the module overview and how to deploy.
117 lines
4 KiB
Markdown
117 lines
4 KiB
Markdown
# Deployment
|
|
|
|
Two compose files live here, for two distinct scenarios:
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `docker-compose.prod.yml` | Pulls pre-built images from GHCR. This is what runs on the home server. |
|
|
| `docker-compose.dev.yml` | Dev stack with Postgres only — auth runs on local credentials. |
|
|
|
|
Images are built by `.github/workflows/build-and-push.yml` on every push to
|
|
`main` and published to:
|
|
|
|
- `ghcr.io/<GHCR_OWNER>/familynido-api:latest`
|
|
- `ghcr.io/<GHCR_OWNER>/familynido-web:latest`
|
|
|
|
Each push also produces an immutable `sha-<shortsha>` tag for rollbacks.
|
|
|
|
## First-time server setup
|
|
|
|
Prerequisites on the server (Ubuntu, Docker, Traefik on the
|
|
`${TRAEFIK_NETWORK}` network with a Let's Encrypt cert resolver).
|
|
|
|
1. **Create a Personal Access Token** on GitHub with scope `read:packages`.
|
|
GHCR is private for private repos, so the server needs to authenticate.
|
|
|
|
2. **Log in to GHCR on the server** (one-time, the credentials are cached
|
|
in `~/.docker/config.json`):
|
|
|
|
```bash
|
|
echo <PAT> | docker login ghcr.io -u <github-username> --password-stdin
|
|
```
|
|
|
|
3. **Prepare the deploy folder** (e.g. `/opt/familynido/`):
|
|
|
|
```bash
|
|
sudo mkdir -p /opt/familynido
|
|
sudo chown $USER:$USER /opt/familynido
|
|
cd /opt/familynido
|
|
```
|
|
|
|
Copy `deploy/docker-compose.prod.yml` and `deploy/.env.example` from
|
|
this repo into that folder, rename the example to `.env`, and fill it in
|
|
with the real PocketId/Postgres credentials.
|
|
|
|
4. **Pull and start**:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yml pull
|
|
docker compose -f docker-compose.prod.yml up -d
|
|
```
|
|
|
|
5. Traefik picks the service up via the labels and serves it at the
|
|
`${TRAEFIK_HOST}` you configured once Let's Encrypt issues the cert.
|
|
|
|
> **OIDC client id.** The default `OIDC_CLIENT_ID` is `familynido`. If you
|
|
> use PocketID or another upstream IdP, create a client with that id (or
|
|
> change the value here to match what your IdP exposes).
|
|
|
|
## Google Calendar credentials
|
|
|
|
The calendar module mirrors events from Google Calendar (read-only). The
|
|
backend needs an OAuth client to drive the consent flow:
|
|
|
|
1. Create a project at https://console.cloud.google.com and enable the
|
|
**Google Calendar API**.
|
|
2. In **APIs & Services → OAuth consent screen**, configure an external app
|
|
and add the scope `https://www.googleapis.com/auth/calendar.readonly`.
|
|
Point the privacy/terms URLs at the pages your deployment serves under
|
|
`/legal/privacidad.html` and `/legal/condiciones.html`. Both live in
|
|
`web/familynido-web/public/legal/` in the repo and are deployed
|
|
automatically with the web image.
|
|
3. In **APIs & Services → Credentials**, create an **OAuth 2.0 Client ID** of
|
|
type *Web application*. Set the authorized redirect URI to exactly
|
|
`https://<your-host>/api/calendar/google/callback`.
|
|
4. Copy the client id and secret into `.env`:
|
|
|
|
```env
|
|
GOOGLE_CLIENT_ID=...
|
|
GOOGLE_CLIENT_SECRET=...
|
|
GOOGLE_OAUTH_REDIRECT_URI=https://<your-host>/api/calendar/google/callback
|
|
```
|
|
|
|
5. Restart the API container so the new env vars are picked up:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.prod.yml up -d --force-recreate api
|
|
```
|
|
|
|
The callback path is already proxied by the existing `/api/` block in
|
|
`deploy/nginx/default.conf`, so no extra reverse-proxy config is needed.
|
|
|
|
## Updating after a push to `main`
|
|
|
|
Until Watchtower is wired up (separate commit), updates are a manual pull:
|
|
|
|
```bash
|
|
cd /opt/familynido
|
|
docker compose -f docker-compose.prod.yml pull
|
|
docker compose -f docker-compose.prod.yml up -d
|
|
```
|
|
|
|
## Rolling back
|
|
|
|
Each build tags the images with `sha-<shortsha>`. To roll back to a previous
|
|
build without reverting the main branch:
|
|
|
|
```bash
|
|
# 1. Pin the images to a known-good SHA in docker-compose.prod.yml,
|
|
# e.g. ghcr.io/.../familynido-api:sha-a57d52e
|
|
# 2. docker compose -f docker-compose.prod.yml up -d
|
|
```
|
|
|
|
## Database migrations
|
|
|
|
Not yet automated. After a deploy that includes a new EF migration, run
|
|
them manually from the repo on the dev machine pointed at the prod DB, or
|
|
(TODO) add `db.Database.Migrate()` to `Program.cs` gated by config.
|