Browse Source

update remote build

imwald
Silberengel 5 days ago
parent
commit
be853e3017
  1. 5
      .env.dist
  2. 81
      README.md
  3. 40
      compose.hub.yaml

5
.env.dist

@ -39,7 +39,10 @@ MYSQL_ROOT_PASSWORD=root_password @@ -39,7 +39,10 @@ MYSQL_ROOT_PASSWORD=root_password
# PREWARM_ON_START=0
# Hub deploy: optional full image ref (default silberengel/unfold:latest in compose.hub.yaml).
# UNFOLD_DOCKER_IMAGE=silberengel/unfold:1.0.0
# Optional extra CLI args for the docker `cron` service (full `app:prewarm` every 10 min). Example: --metadata-limit=100 --no-magazine
# Optional extra CLI args for the docker `cron` service (dev) and `prewarm` service (compose.hub.yaml):
# full `app:prewarm` every 10 min. Example: --metadata-limit=100 --no-magazine
# After changing, recreate: `docker compose up -d --force-recreate cron` (dev) or
# `docker compose -f compose.hub.yaml up -d --force-recreate prewarm` (hub).
# PREWARM_FLAGS=
# compose.hub.yaml: default host port is 9080. Use 80 only if nothing else binds it. Loopback-only example:
# HTTP_PUBLISH=127.0.0.1:9080

81
README.md

@ -55,7 +55,7 @@ make prewarm @@ -55,7 +55,7 @@ make prewarm
| 3 | `articles:get -- '-2 month' 'now'` — sync long-form into MySQL for that window |
| 4 | `app:prewarm` — magazine **30040**, **kind-0** profiles, **comment** cache (default **`--comments-max=10`**, newest by `createdAt`) |
`make prewarm` brings the stack (including `cron`) up so scheduled prewarm is active. **Optional** extra arguments for the **cron**-scheduled `app:prewarm` go in **`.env`** as **`PREWARM_FLAGS`** (same as you might pass to `php bin/console app:prewarm …`); Compose passes them into the `cron` container. Example: `PREWARM_FLAGS="--metadata-limit=50 --no-magazine"`. **Restart** the `cron` service after changing `PREWARM_FLAGS` so the container reloads the env. Hub / `compose.hub.yaml` has no `cron` service; use a host timer or `exec` if you need the same there.
`make prewarm` brings the stack (including `cron`) up so scheduled prewarm is active. **Optional** extra arguments for the **cron**-scheduled `app:prewarm` go in **`.env`** as **`PREWARM_FLAGS`** (same as you might pass to `php bin/console app:prewarm …`); Compose passes them into the `cron` container. Example: `PREWARM_FLAGS="--metadata-limit=50 --no-magazine"`. **Restart** the `cron` service after changing `PREWARM_FLAGS` so the container reloads the env. On the **hub** stack, the `prewarm` service reads the same `PREWARM_FLAGS`; use `docker compose -f compose.hub.yaml up -d --force-recreate prewarm` after changing it.
---
@ -107,15 +107,84 @@ For a full **Nostr backfill** + one-shot prewarm, use **`make prewarm`** (or a h @@ -107,15 +107,84 @@ For a full **Nostr backfill** + one-shot prewarm, use **`make prewarm`** (or a h
---
## Production / Hub image
## Production / Hub (remote server)
The app runs as a **pre-built** image (no app source on the server). The server only needs `compose.hub.yaml`, a `.env`, and Docker. Default image: `silberengel/unfold:latest`; override with **`UNFOLD_DOCKER_IMAGE`**.
| Topic | Notes |
|-------|--------|
| `compose.hub.yaml` | Runs a **pulled** image (default `silberengel/unfold:latest`), no local PHP app build. Override with `UNFOLD_DOCKER_IMAGE`. |
| HTTP publish | `HTTP_PUBLISH` in `.env` (default **9080** → container **80**). Set `TRUSTED_PROXIES` behind a reverse proxy. |
| Secrets | Set `APP_SECRET` and DB credentials in **real** env; do not commit production secrets. |
| `compose.hub.yaml` | Defines **`php`** (FrankenPHP) + **`database`** (MySQL) + **`prewarm`** (same app image: **`app:prewarm` every 10 minutes**, like dev’s `docker/cron`). Optional: disable `prewarm` in Compose if you prefer a host `cron` only. |
| HTTP | **`HTTP_PUBLISH`** in `.env` maps **host** port → container **80** (default **9080**). Put a reverse proxy (e.g. Apache) in front; set **`TRUSTED_PROXIES`** to match your proxy (often include `127.0.0.0/8` and the Docker bridge CIDR, e.g. `172.16.0.0/12`). |
| Secrets | Real **`APP_SECRET`** and **`MYSQL_*`** (or external DB via `DATABASE_URL` if you change the file). Do not commit production `.env`. |
| `PREWARM_FLAGS` | Optional extra CLI args for the hub **`prewarm`** service (and dev **`cron`**). After editing `.env`, run `docker compose -f compose.hub.yaml up -d --force-recreate prewarm`. |
### Build, tag, and push (on your machine or CI)
From the **repository root** (same `Dockerfile` as local prod):
```bash
# Production image
docker build --platform linux/amd64 --target frankenphp_prod -t YOUR_REGISTRY/unfold:latest .
# Optional: immutable tag for rollbacks
docker build --platform linux/amd64 --target frankenphp_prod -t YOUR_REGISTRY/unfold:1.0.0 .
# Push what you use on the server
docker push YOUR_REGISTRY/unfold:latest
docker push YOUR_REGISTRY/unfold:1.0.0
```
- Use **`linux/amd64`** if the server is amd64; use **`arm64`** (or a matching `--platform`) for arm servers.
- The image name must match what the server will pull: either keep **`UNFOLD_DOCKER_IMAGE=YOUR_REGISTRY/unfold:TAG`** in server `.env`, or push to the default name **`silberengel/unfold:latest`**.
### Deploy on the server (pull, up, migrate)
In a directory that contains **only** `compose.hub.yaml` and your **`.env`** (e.g. `~/tmp/unfold`):
```bash
cd /path/to/deploy
docker compose -f compose.hub.yaml pull
docker compose -f compose.hub.yaml up -d
docker compose -f compose.hub.yaml exec php php bin/console doctrine:migrations:migrate --no-interaction
```
After code changes: **`pull``up -d`**; run **migrations** when the repo added new migration files.
**Optional image / tag** (in `.env` or one-shot):
```bash
export UNFOLD_DOCKER_IMAGE=YOUR_REGISTRY/unfold:1.0.0
docker compose -f compose.hub.yaml up -d
```
### One-time Nostr backfill (equivalent to `make prewarm` on dev)
`compose.hub` has no bind-mounted repo, so run the same commands **inside the `php` container** (after the stack is up and migrations have run):
```bash
docker compose -f compose.hub.yaml exec -T php php bin/console articles:get -- '-2 month' 'now'
docker compose -f compose.hub.yaml exec -T php php bin/console app:prewarm
```
Adjust the **articles:get** window as needed.
### Scheduled `app:prewarm` on hub
The **`prewarm`** service in `compose.hub.yaml` uses the **same** image as `php` and runs **`app:prewarm` every 10 minutes** (same cadence as dev’s `docker/cron`). It starts only after the **database** is healthy and the **`php`** service passes its healthcheck (so migrations from the `php` entrypoint have typically completed). **Optional** `PREWARM_FLAGS` in `.env` is passed into that container; after changing it, run:
```bash
docker compose -f compose.hub.yaml up -d --force-recreate prewarm
```
**If you do not** want a Compose sidecar (e.g. to save RAM), stop and disable the `prewarm` service and use **host** `cron` or **systemd** instead:
```text
*/10 * * * * cd /path/to/deploy && docker compose -f compose.hub.yaml exec -T php php bin/console app:prewarm
```
**`PREWARM_ON_START=1`** on the `php` service only warms **once** at container start, not on a schedule.
File header in `compose.hub.yaml` lists pull, migrate, and optional build/push one-liners.
The file `compose.hub.yaml` in the repo repeats minimal pull/migrate/build one-liners in its header for quick copy-paste.
---

40
compose.hub.yaml

@ -5,6 +5,9 @@ @@ -5,6 +5,9 @@
# docker compose -f compose.hub.yaml up -d
# docker compose -f compose.hub.yaml exec php php bin/console doctrine:migrations:migrate --no-interaction
#
# Services: `php` (web), `database` (MySQL), `prewarm` (same image; `app:prewarm` every 10 min — see README).
# Optional: PREWARM_FLAGS in .env (same as dev `cron` service), then `docker compose up -d --force-recreate prewarm`.
#
# Required in .env: APP_SECRET. Set MYSQL_* (or replace DATABASE_URL after editing this file) if you
# use the bundled database. For TLS in front, set TRUSTED_PROXIES to include your reverse proxy CIDR.
#
@ -35,10 +38,47 @@ services: @@ -35,10 +38,47 @@ services:
- caddy_config:/config
ports:
- "${HTTP_PUBLISH:-9080}:80/tcp"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1/", "-o", "/dev/null"]
interval: 10s
timeout: 5s
retries: 6
start_period: 120s
depends_on:
database:
condition: service_healthy
prewarm:
image: ${UNFOLD_DOCKER_IMAGE:-silberengel/unfold:latest}
pull_policy: always
restart: unless-stopped
working_dir: /app
# Same app image as `php`, but not FrankenPHP: wait for DB + `php` healthy (migrations done), then
# run `app:prewarm` every 10 minutes (dev `docker/cron` uses the same interval).
entrypoint: ["/bin/sh", "-c"]
command:
- |
until php bin/console dbal:run-sql -q "SELECT 1" 2>/dev/null; do
echo "prewarm: waiting for database..."
sleep 2
done
while true; do
sleep 600
php bin/console app:prewarm $${PREWARM_FLAGS-} || true
done
environment:
APP_ENV: ${APP_ENV:-prod}
APP_SECRET: ${APP_SECRET}
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.0/8,10.0.0.0/8}
SERVER_NAME: ${SERVER_NAME:-:80}
DATABASE_URL: mysql://${MYSQL_USER:-unfold_user}:${MYSQL_PASSWORD:-password}@database:3306/${MYSQL_DATABASE:-unfold_db}?serverVersion=${MYSQL_VERSION:-8.0}&charset=${MYSQL_CHARSET:-utf8mb4}
PREWARM_FLAGS: ${PREWARM_FLAGS:-}
depends_on:
database:
condition: service_healthy
php:
condition: service_healthy
database:
image: mysql:${MYSQL_VERSION:-8.0}
restart: unless-stopped

Loading…
Cancel
Save