diff --git a/DOCKER.md b/DOCKER.md index 1f280d97..d384dfa4 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -2,6 +2,9 @@ Run 9Router in a container. Published image: [`decolua/9router`](https://hub.docker.com/r/decolua/9router) — multi-platform `linux/amd64` + `linux/arm64`. +For Git-based Dokploy deployments with a persistent named volume, see +[`docs/DOKPLOY.md`](docs/DOKPLOY.md). + --- # 👤 For Users diff --git a/docs/DOKPLOY.md b/docs/DOKPLOY.md new file mode 100644 index 00000000..3eb4a4c1 --- /dev/null +++ b/docs/DOKPLOY.md @@ -0,0 +1,91 @@ +# Deploying 9Router with Dokploy + +This guide deploys 9Router from this Git repository and keeps its application +data through every rebuild and container replacement. + +## Persistence design + +The Compose stack mounts the named Docker volume `9router-data` at `/app/data`. +`DATA_DIR` is explicitly set to that location. The application stores its +SQLite database, generated JWT secret, database migration backups, tunnel +state, and other runtime state there. + +```text +Docker volume: 9router-data +Container path: /app/data +Database: /app/data/db/data.sqlite +Backups: /app/data/db/backups/ +JWT secret: /app/data/jwt-secret +``` + +Dokploy replaces the container when a new commit is deployed; named Docker +volumes survive that operation. **Do not change `DATA_VOLUME_NAME` after the +first successful deployment**, or Dokploy will mount a new, empty volume. + +## 1. Create the Dokploy application + +1. Create a **Compose** application in Dokploy and connect this repository. +2. Select the branch that should deploy (for example, `main`). +3. Use the repository root and `docker-compose.yml` as the Compose file. +4. Enable automatic deployment on pushes for the selected branch. + +The Compose file contains a `build` section, so every deploy builds the image +from the exact checked-out commit rather than pulling a published image. + +## 2. Configure environment variables + +In the Dokploy application's **Environment** settings, add the following +values. Keep these values in Dokploy; do not commit an `.env` file. + +| Variable | Required | Value | +| --- | --- | --- | +| `JWT_SECRET` | Yes | A unique random value, for example `openssl rand -hex 32` | +| `INITIAL_PASSWORD` | Yes | A strong password used for the first login | +| `API_KEY_SECRET` | Yes | A unique random value, for example `openssl rand -hex 32` | +| `MACHINE_ID_SALT` | Yes | A unique random value, for example `openssl rand -hex 32` | +| `DATA_VOLUME_NAME` | Recommended | `9router-data` — choose a unique stable name per environment | +| `AUTH_COOKIE_SECURE` | Recommended | `true` when serving through an HTTPS domain | +| `IMAGE_TAG` | Optional | A stable label such as `dokploy` | + +`DATA_DIR`, `PORT`, `HOSTNAME`, and `NODE_ENV` are intentionally fixed in +`docker-compose.yml`; do not override them in Dokploy. + +The bundled `headroom` service is retained for token compression. It does not +hold 9Router application data. To use a different Headroom endpoint, set +`HEADROOM_URL` in Dokploy. + +## 3. Attach a domain + +Add the application's public domain in Dokploy and target the `9router` +service on internal port `20128`. Let Dokploy/Traefik terminate TLS. With TLS +enabled, retain `AUTH_COOKIE_SECURE=true`. + +## 4. Deploy and verify persistence + +1. Run the first deployment, open `/dashboard`, and sign in. +2. Add a provider connection or a model combo, then change a setting. +3. Push a harmless commit to the configured branch. +4. Wait for Dokploy's automatic deployment to finish and sign in again. +5. Verify the provider, combo, and setting are still present. + +If the dashboard is empty after a redeploy, check that the running service has +both `DATA_DIR=/app/data` and the original `DATA_VOLUME_NAME`. Do not delete +the `9router-data` Docker volume when deleting/recreating the application. + +## Backup and restore + +The data volume is persistent but is not a backup. Take regular, off-host +copies of the entire `/app/data` directory, especially +`/app/data/db/data.sqlite`. Include the `jwt-secret` file so existing browser +sessions remain valid after a disaster recovery. + +Before a manual restore: + +1. Stop the Dokploy application so SQLite is not being written. +2. Restore the saved contents into the existing `DATA_VOLUME_NAME` volume. +3. Ensure the restored files are writable by the container's `node` user. +4. Start the application and confirm `/api/health` returns `{ "ok": true }`. + +Never run two 9Router containers against the same SQLite volume at once. +SQLite is suitable for this single-instance deployment, not shared multi-node +storage.