Event photo upload app with OAuth2 (Google Drive), Caddy route, and env-var-driven compose config. Refresh token persists to token.json so re-auth isn't needed after container restarts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
83 lines
3.2 KiB
Markdown
83 lines
3.2 KiB
Markdown
# carloselugo.com — Docker Stack
|
||
|
||
Self-hosted infrastructure running on a single server, reverse-proxied by Caddy with automatic HTTPS.
|
||
|
||
## Services
|
||
|
||
| Service | URL | Description |
|
||
|---|---|---|
|
||
| Frontend | [carloselugo.com](https://carloselugo.com) | Personal site — Vite + nginx (multi-stage build) |
|
||
| Uptime Kuma | [uptime.carloselugo.com](https://uptime.carloselugo.com) | Uptime monitoring |
|
||
| Netdata | [metrics.carloselugo.com](https://metrics.carloselugo.com) | System metrics |
|
||
| ntfy | [ntfy.carloselugo.com](https://ntfy.carloselugo.com) | Push notifications |
|
||
| Dozzle | [logs.carloselugo.com](https://logs.carloselugo.com) | Container log viewer |
|
||
| Gitea | [git.carloselugo.com](https://git.carloselugo.com) | Self-hosted Git |
|
||
| Fotos | [fotos.carloselugo.com](https://fotos.carloselugo.com) | Event photo upload → Google Drive (OAuth2, admin panel at `/admin`) |
|
||
|
||
> uptime, metrics, and logs are restricted to whitelisted IPs + basic auth.
|
||
|
||
## Structure
|
||
|
||
```
|
||
/opt/web/
|
||
├── Caddyfile # Reverse proxy config (TLS, routing, auth)
|
||
├── docker-compose.yml # All services
|
||
├── frontend/ # Personal site source
|
||
│ ├── Dockerfile
|
||
│ ├── src/
|
||
│ └── nginx.conf
|
||
├── ntfy/
|
||
│ └── config/
|
||
│ └── server.yml
|
||
└── dozzle/
|
||
└── data/
|
||
```
|
||
|
||
## Stack
|
||
|
||
- **Reverse proxy** — Caddy 2 (automatic HTTPS via Let's Encrypt, HTTP/3)
|
||
- **Frontend** — Vite build → nginx:alpine
|
||
- **Git** — Gitea (SQLite, SSH on port 2222)
|
||
- **Monitoring** — Netdata (host metrics) + Uptime Kuma (endpoint checks)
|
||
- **Notifications** — ntfy (push to mobile, auth-protected)
|
||
- **Logs** — Dozzle
|
||
|
||
## Usage
|
||
|
||
```bash
|
||
# Start all services
|
||
docker compose up -d
|
||
|
||
# Start a single service
|
||
docker compose up -d gitea
|
||
|
||
# View logs
|
||
docker compose logs -f caddy
|
||
|
||
# Reload Caddy config without downtime
|
||
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
||
```
|
||
|
||
## Git workflow
|
||
|
||
Push to this repo from the server:
|
||
|
||
```bash
|
||
git remote add origin ssh://git@localhost:2222/carlitosbond/web.git
|
||
git push
|
||
```
|
||
|
||
From an external machine (port 2222 must be open in firewall):
|
||
|
||
```bash
|
||
git remote add origin ssh://git@git.carloselugo.com:2222/carlitosbond/web.git
|
||
git push
|
||
```
|
||
|
||
## Notes
|
||
|
||
- Umami analytics is disabled (commented out in `docker-compose.yml`) — uncomment to re-enable.
|
||
- Netdata is pinned to CPUs 0–1 with low CPU priority to avoid impacting other workloads.
|
||
- ntfy denies all access by default — users must be created with `ntfy user add`.
|
||
- The `fotos` service reads its secrets from `/opt/web/.env` (compose-level, gitignored) — `DRIVE_FOLDER_ID`, `ADMIN_PASSWORD`, `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_OAUTH_REFRESH_TOKEN`, `OAUTH_REDIRECT_URI`. The live refresh token is persisted separately in `photo-upload/token.json` (bind-mounted, gitignored, overwrites the bootstrap `.env` value once regenerated from `/admin`). The OAuth client in Google Cloud Console must have `OAUTH_REDIRECT_URI`'s exact value registered under Authorized redirect URIs, and — while the app is in Testing publishing status — the signing-in Google account must be added under Audience → Test users, or Google blocks the callback.
|