Author SHA1 Message Date
fsylvain 7f1e8e572d docs: deployement guide with new ui update settings 2026-10-02 17:11:04 +02:00
3 changed files with 88 additions and 8 deletions

No files matched your search

+4
View File
@@ -15,6 +15,10 @@ cp .env.example .env # fill in PUBLIC_URL, JWT_SECRET and SMTP_*
Data is stored in `./data/` by default. For a real server (systemd, HTTPS, backups), see **[docs/deployment.md](docs/deployment.md)**.
### Updates from the browser
On Linux with systemd, install v2.6.0-beta or newer manually, then follow [the browser-update setup guide](docs/deployment.md#browser-updates). It covers finding the actual database directory, installing the helper, testing a restart and troubleshooting. Other platforms use manual upgrades.
### Develop
Requires Go 1.27+, Node.js 22, [Bun](https://bun.sh) and [Task](https://taskfile.dev).
+2
View File
@@ -34,6 +34,8 @@ Copy [`.env.example`](../.env.example) to `.env` to get started. When a required
Relative paths are resolved against the process's working directory. In production, use absolute paths (see [deployment.md](deployment.md)).
For example, `WorkingDirectory=/var/lib/renewcms` with `DB_FILE=./data/renewcms.db` puts the database in `/var/lib/renewcms/data`. Browser-update `--data-dir` must name that directory, because the app reads helper status and update records beside the database. It is not necessarily `WorkingDirectory` or `ReadWritePaths`. Do not change an existing `DB_FILE` merely to match the helper: that could open a different, empty database. See [browser-update setup](deployment.md#browser-updates).
## Adding a variable
1. Read it in the code where it is needed (infrastructure or `api/`, never `internal/domain`).
+82 -8
View File
@@ -8,7 +8,7 @@ RenewCMS ships as **one self-contained executable**. It serves:
| `/healthz` | Liveness check, returns `200 ok` |
| `/*` | The Vue SPA (embedded), with fallback to `index.html` for client-side routes |
Its only runtime state is the SQLite database (`DB_FILE`) and the uploads directory (`UPLOAD_DIR`). Nothing else needs to be installed on the server.
Runtime state includes the SQLite database (`DB_FILE`), uploads (`UPLOAD_DIR`), and update records, staged binaries and backups beside the database. Running a release binary needs no Go, Bun or Node installation. Browser updates additionally require Linux with systemd and the helper setup below.
## 1. Build
@@ -26,7 +26,7 @@ The frontend is built once and embedded into every binary. The binaries are stat
### Release signing key (maintainers, once)
1. Run `task release:keygen -- <path>` with a path outside the repository (for example `~/.renewcms/release.key`). The private key is written there and never leaves your machine; keygen refuses a path inside the work tree.
2. Commit the printed public key as `internal/infrastructure/update/release.pub`. Until you do, the file holds a placeholder (32 zero bytes) and `task release` fails with a message saying so.
2. Commit the printed public key as `internal/infrastructure/update/release.pub`. A placeholder key (32 zero bytes) prevents signing. Official releases already embed the project's public key; this setup is for release maintainers, not site operators. Never copy the private signing key to a CMS server.
3. Export `RELEASE_SIGNING_KEY=<path to the private key>` before every `task release`.
To check a download by hand: `go run ./cmd/releasesign verify checksums.txt`, with `checksums.txt.sig` next to it, then compare the binary's hash with the matching line of `checksums.txt`.
@@ -43,13 +43,39 @@ Once the restart helper is installed (it writes `update-helper.json` next to `DB
The owner's Restart button installs a downloaded update, or just restarts the site, without touching the server by hand. The app itself stays unprivileged: the systemd unit below keeps `ProtectSystem=strict` and only the data directory (the directory that holds `DB_FILE`) is writable. The only privileged code is a small helper, the same binary started as `renewcms apply-update` by a systemd service that runs as root.
Enable it once, on the server, as root:
First install v2.6.0-beta or newer manually, back up the database, uploads and current binary, and confirm the app runs as an unprivileged service user. The binary and its parent directory should be controlled by root. Owners can start updates and restarts; admins have read-only update status.
#### Find the actual database directory
The helper's `--data-dir` must be the directory containing `DB_FILE`. Inspect the relevant settings without printing the configuration's secrets:
```sh
sudo renewcms enable-browser-updates
sudo systemctl show renewcms -p User -p WorkingDirectory
sudo grep '^DB_FILE=' /etc/renewcms/renewcms.env
```
The command reads the service name, the binary path and the data directory from your `renewcms.service` unit. Pass `--service`, `--binary`, `--data-dir` and `--port` to override them or when there is no such unit. The port, used by the helper to poll `/healthz`, comes from `--port`, else from `PORT` in the unit's `EnvironmentFile`, else 8080. It then:
| WorkingDirectory | DB_FILE | Correct --data-dir |
| --- | --- | --- |
| `/var/lib/renewcms` | `/var/lib/renewcms/renewcms.db` | `/var/lib/renewcms` |
| `/var/lib/renewcms` | `./data/renewcms.db` | `/var/lib/renewcms/data` |
Leave an existing `DB_FILE` unchanged: changing it can open a different, empty database. Configure the helper to match the existing database instead.
#### Enable the helper
For this guide's absolute database path and port 8080, run:
```sh
sudo /opt/renewcms/renewcms enable-browser-updates \
--service renewcms \
--binary /opt/renewcms/renewcms \
--data-dir /var/lib/renewcms \
--port 8080
```
Replace these flags with your actual paths and port. For the relative database example above and port 8788, use `--data-dir /var/lib/renewcms/data --port 8788`. The absolute executable path works even when `/opt/renewcms` is absent from sudo's PATH.
Without explicit flags, discovery reads `/etc/systemd/system/renewcms.service` and takes its first `ReadWritePaths` entry as the data directory. It does not resolve `DB_FILE`, so explicit flags are necessary when these directories differ or the unit lives elsewhere. The port comes from `--port`, else `PORT` in the unit's `EnvironmentFile`, else 8080. The helper polls `/healthz` on `127.0.0.1` at that port. It then:
- writes `renewcms-update.path` and `renewcms-update.service` next to your other units in `/etc/systemd/system` (no secret and no environment value goes into them);
- runs `systemctl daemon-reload` and enables and starts the path unit;
@@ -57,13 +83,57 @@ The command reads the service name, the binary path and the data directory from
Running it again rewrites the units and the status file to the current values. If a step fails, the units it wrote are removed again and `update-helper.json` is not written.
To undo it:
#### Verify setup and test a plain restart
For the layout above, run these checks, substituting your service user and database directory if different:
```sh
sudo renewcms disable-browser-updates
systemctl is-active renewcms-update.path
systemctl is-enabled renewcms-update.path
sudo systemctl cat renewcms-update.service
sudo -u renewcms cat /var/lib/renewcms/update-helper.json
```
This stops and disables the path unit, removes both unit files and `update-helper.json`, and runs `systemctl daemon-reload`. The restart request, the result, the staged files and the backups stay where they are, and the Settings page goes back to "Not prepared".
Expect `active`, `enabled`, an `ExecStart` with the correct paths and port, and readable JSON. Reload Settings as owner. With no update waiting, Restart should appear. Press it, confirm the plain restart dialog, and expect the overlay followed by the site returning and a “Site restarted” message.
Then run:
```sh
systemctl is-active renewcms
sudo cat /var/lib/renewcms/update-result.json
sudo cat /var/lib/renewcms/update-running.json
sudo journalctl -u renewcms-update.service -n 30 --no-pager
curl --retry 5 --retry-connrefused --retry-delay 2 -fsS http://127.0.0.1:8080/healthz
```
Expect an active app service, result `kind: restart` with `outcome: succeeded`, the installed version and a new process in `update-running.json`, and health response `ok`. Check `/healthz` through your public HTTPS hostname too. A brief connection refusal during startup can be transient. The oneshot helper normally becomes inactive after success; the path unit stays active.
#### Install a future update
When a newer signed release exists, check for updates in Settings, read its notes and click Update. The site stays online while the files are downloaded, verified and backed up. Wait for Ready to install, then click Restart and confirm installation. Afterwards, check the version in Settings, public health, the helper journal and `update-result.json`: expect `kind: update` and `outcome: succeeded`.
On the latest release you can test only a plain restart. Test installation, forced rollback and hostile-file cases on an isolated Linux/systemd machine with its own data and service. The helper unit names are fixed, so setting up another instance on the same machine replaces that machine's existing helper configuration.
#### Troubleshooting
- **Restart is missing:** reload Settings, confirm you are owner, and inspect `GET /v1/system/update` in the browser's Network panel. For an idle owner, `can_apply: false` means the app did not detect the helper. Check that `update-helper.json` is beside the actual database and readable by the service user.
- **Wrong helper directory:** disable the helper with the old `--data-dir`, then enable it with the correct directory and port. Do not change `DB_FILE` to compensate.
- **Overlay does not clear:** inspect the helper journal, `update-result.json` and `update-running.json` in the actual database directory. Check localhost health on the configured port, then public health through the proxy. Do not cache `/healthz` at the proxy.
- **Old binary panics on `--version`:** older releases may interpret it as normal startup and require server environment variables. v2.6.0-beta and newer print their version without loading configuration. A separately launched process failing this way does not stop the systemd service.
- **Download or verification fails:** inspect the app journal. All assets and redirects must use HTTPS on the configured feed host. The release must include the six binaries, `checksums.txt` and `checksums.txt.sig`.
- **Update rolls back:** check the previous version and helper journal. Automatic rollback restores the binary, not the database; schema changes still require a recovery plan.
#### Disable the helper
Use the same database directory you supplied when enabling it:
```sh
sudo /opt/renewcms/renewcms disable-browser-updates \
--service renewcms \
--data-dir /var/lib/renewcms
```
This stops and disables the path unit, removes both unit files and `update-helper.json`, and runs `systemctl daemon-reload`. The restart request, result, staged files and backups stay. `can_apply` becomes false: Settings shows “Not prepared” when an update is available, or “You're up to date” without Restart otherwise.
How it works: pressing Restart makes the app write `update-restart-request.json` in the data directory. The path unit notices it and starts the helper, which deletes the request, checks the staged files again, installs the update, restarts the service and waits up to 60 seconds for the site to answer `/healthz` and for the new process to write `update-running.json`. The helper writes its outcome to `update-result.json`; the Settings page reads it. The technical reason of a failure goes to the journal only (`journalctl -u renewcms-update`).
@@ -85,6 +155,10 @@ What is supported and what is not:
- A plain restart (no update waiting) restarts the service the same way and records `succeeded` or `failed`; no binary is replaced.
- Only the owner can press Restart. The site is unavailable for about 10 seconds.
#### Verification status
A real v2.6.0-beta plain browser restart was confirmed on Linux/systemd on 2026-10-02: the path unit triggered the helper, a new server process started, the result was `succeeded`, and public health returned `ok`. Real signed installation, forced rollback and adversarial filesystem checks remain unverified. The manual cases are listed in [the restart QA procedure](../features/updates_restart_helper.qa.md).
## 2. Lay out the server
The following is a suggested layout for Linux: