Files
Magent/docs/PUBLIC_RELEASE.md
T

264 lines
15 KiB
Markdown

# Public installation and release guide
Magent runs as one non-root Linux container containing the Python API and the
Next.js frontend. Connect your own media services in the setup wizard; no
pre-existing Magent account or database is required. Optional
integrations may be skipped. Media files remain in your existing media services.
The lightweight Dockerfile builds the frontend separately and copies only its
standalone runtime, static assets and public files into the final image. Build
tools, the full development dependency tree and package-manager caches are not
runtime requirements. The image still needs both Python and Node to serve the
API and frontend; it is not a static website.
Both build and runtime stages use Alpine Linux, so native dependencies are built
for its musl runtime rather than copied from an incompatible glibc image.
The non-root UID/GID and `/app/data` layout are unchanged. Existing installations
do not need new databases, replacement keys or new data volumes for this change.
Magent's own source code is licensed under the [MIT License](../LICENSE).
Bundled dependencies retain their own licenses; Magent's license does not
relicense third-party software or grant rights to third-party branding.
## Fresh installation
For the simplest **Portainer** installation, use the image-only root
[compose.yml](../compose.yml) and follow the [Portainer guide](PORTAINER.md).
That fresh-install path pulls `rephl3xnz/magent:latest` from Docker Hub with no
environment inputs, automatically persists its private keys and requires no
Dockerfile or `.env`. Confirm the browser-facing application URL in the
token-authorized first-administrator wizard; matching CORS and cookie security
follow the saved URL automatically. Managed SQLite stays at
`/app/data/magent.db`, and API documentation stays disabled.
The managed-install image is published on Docker Hub. Repository changes alone
do not update an existing deployment: explicitly pull the image when upgrading,
retaining the same persistent volume. `latest` is mutable, so record the resolved
digest before an upgrade; older cached images may lack this setup workflow.
The Compose runtime-security block stays enabled and must be left unchanged.
See the [complete environment reference](ENVIRONMENT.md) for all supported
variables and the difference between managed, manual and tooling controls.
### Advanced/manual secrets
The procedure below remains supported for operators who manage their own
deployment keys. It uses `docker-compose.hub.yml`, not the zero-input root
`compose.yml`; its required environment inputs are specific to this manual path.
Prerequisites: Docker Engine or Docker Desktop running Linux containers, Docker
Compose v2, and an image release published for your host's architecture. Use the
Compose and environment examples from the **same release** as the image.
1. Put `docker-compose.hub.yml` and a copy of `.env.example` named `.env` in a
private deployment directory. Keep the directory and Compose project name
stable: the project owns the persistent named volume. On Linux restrict `.env`
to the deployment account, for example `chmod 600 .env`; on Windows restrict
its file permissions. Do not commit it, paste it into support reports or
expose it through a web server.
2. Set `MAGENT_IMAGE` to the release's published immutable tag, for example
`rephl3xnz/magent:prod-<actual-commit>`, or its published
`rephl3xnz/magent@sha256:<actual-digest>`. Replace the angle-bracket placeholders.
There is deliberately no implicit `latest` fallback. Confirm the selected
release contains the setup/backup features before following this guide.
3. Generate three **independent** values. Run these on a trusted machine; the
commands print secrets, so do not share their output or put it in CI logs:
```bash
# JWT_SECRET
python -c "import secrets; print(secrets.token_urlsafe(48))"
# SETUP_TOKEN (not the JWT secret)
python -c "import secrets; print(secrets.token_urlsafe(48))"
# SETTINGS_ENCRYPTION_KEY (Fernet-compatible, using only Python's standard library)
python -c "import base64, secrets; print(base64.urlsafe_b64encode(secrets.token_bytes(32)).decode())"
```
Put each value in its matching `.env` field. Leave `ADMIN_PASSWORD` blank to
create the administrator through the wizard. There is no shared default
administrator password. Save the signing and encryption keys in a separate
secure backup; generating replacement keys is **not** an upgrade step.
4. For a local trial, keep `MAGENT_BIND_ADDRESS=127.0.0.1`,
`MAGENT_HTTP_PORT=3000`, the localhost URLs and `AUTH_COOKIE_SECURE=false`.
For internet access, configure HTTPS as described below **before** inviting
users. If changing the local port, update both browser-facing URLs too.
5. Validate without printing expanded secrets, pull and start:
```bash
docker compose -p magent -f docker-compose.hub.yml config --quiet
docker compose -p magent -f docker-compose.hub.yml pull magent
docker compose -p magent -f docker-compose.hub.yml up -d --no-build magent
docker compose -p magent -f docker-compose.hub.yml ps
```
6. Open `http://localhost:3000` on the Docker host (or your configured HTTPS
address). The fresh database opens `/setup`. Enter the setup token, create
your administrator, connect the apps you use, review preferences and finish.
App URLs must be reachable **from the container**; `localhost` means Magent
itself, not another container or the Docker host. Use your internal DNS,
reachable LAN addresses or service names on an explicitly shared Docker
network.
7. Remove `SETUP_TOKEN` from `.env` and recreate only Magent using the same
`up -d --no-build magent` command. Setup progress and accounts remain in the
volume. Make and test a backup before relying on the installation.
The template creates a project-scoped named volume at `/app/data`. The image's
data directory is prepared for UID/GID `1000:1000`, avoiding the fresh root-owned
bind-directory problem. The container runs with a read-only root filesystem,
dropped capabilities and private temporary writable areas. The backend's port
8000 is not published: browser API calls use `/api` on the frontend port.
Do not run `docker compose down --volumes` unless you intend to erase the data.
## HTTPS, public URLs and reverse proxies
For the **managed Portainer path**, visit the intended HTTPS address and confirm
it in first-administrator setup. Later application URL changes belong in admin
configuration. CORS and Secure cookies follow the saved URL; no environment
values are required. Setup does not provision DNS, certificates or a proxy.
For the **advanced/manual path**, set these deployment values to **your own**
origin, without a path or trailing slash:
```dotenv
CORS_ALLOW_ORIGIN=https://magent.example.com
MAGENT_APPLICATION_URL=https://magent.example.com
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAMESITE=strict
```
The supplied image already routes browser `/api/*` calls to its internal API.
Do not expose port 8000, set a public API hostname or override the internal
backend URL for this combined-image deployment. Runtime environment changes do
not rebuild the frontend's compiled routing configuration.
With a reverse proxy running directly on the **same host**, use a loopback
bind and route the whole hostname, including `/api`, to `127.0.0.1:3000` (or your
chosen host port). The manual template defaults to loopback; in the root
Portainer template deliberately change its port mapping to `127.0.0.1:3000:3000`
for this topology. For example, a host-running Caddy instance can use:
```caddyfile
magent.example.com {
reverse_proxy 127.0.0.1:3000
}
```
For a containerized proxy, loopback inside that proxy is not the Docker host.
Attach the proxy and Magent to an intentionally shared Docker network and proxy
to `magent:3000`, or configure a reachable host address explicitly. If the proxy
is on another machine, bind the frontend host port to the Docker host's private
interface address and restrict access with a firewall to the trusted proxy. In
the root Portainer template edit the port mapping directly; the manual Hub
template instead supports `MAGENT_BIND_ADDRESS`.
Avoid exposing all interfaces merely to work around routing.
Provide valid TLS, suitable DNS and upload limits of at least 34 MiB for backup
restore. Do not cache login, setup, authenticated pages or API responses at the
proxy/CDN. Keep the exact public origin configured: the request-origin guard
does not trust arbitrary `Host` or forwarded headers. Keep `API_DOCS_ENABLED=false`
for public service. TLS terminates at your proxy, not inside this image.
## Existing installations and upgrades
**Do not replace an existing deployment with the fresh named-volume template.**
Keep its existing project name, mount, database path, environment, signing key
and encryption key. Switching from `./data:/app/data` to a new named volume makes
an existing installation look empty; it does not migrate data. Never complete
fresh setup to fix a missing mount.
For existing Linux bind mounts, confirm the exact intended data directory and
its files are writable by UID/GID `1000:1000`. Back up first and correct only that
application directory if needed; do not recursively change an entire shared
stack, host directory or filesystem. Do not work around permissions by running
Magent as root. Custom UID/GID images require matching ownership.
Before upgrading:
1. Save the old image tag **and digest**, Compose definition and protected
environment/key backup. Create a consistent data backup and verify recovery.
2. Read the target release's migration notes and select its immutable image.
Pull it before interrupting service. Validate the actual saved deployment
definition, including a Portainer stack's saved environment if used; editing
a separate host Compose file does not update Portainer's copy.
3. Recreate **only Magent**, retaining the original data mount and secrets. For
this template use `docker compose -p magent -f docker-compose.hub.yml up -d
--no-deps --no-build magent`. Use your actual project/file for other stacks.
4. Verify health, real local-account login, any enabled Jellyfin login, requests,
app connections and invite behaviour. Do not trigger imports, notifications
or destructive repair actions merely as a smoke test.
The zero-input Portainer template deliberately uses `latest`. On that path,
review the new release and pull its updated image when updating the existing
stack; restarting alone leaves the current image in use. Save the previous
digest because `latest` can move. Operators preferring controlled releases may
replace its literal `image:` value with a published immutable tag/digest without
changing the volume or generated keys.
For rollback, select the recorded image and recreate only Magent. Database
migrations may prevent older versions from reading newer data: follow the
release's compatibility notes and restore the matching backup if required.
Restoring old data discards changes since that backup. Never restore a whole
shared stack file over unrelated service changes.
## Backups and recovery
For managed Portainer installations, retain `bootstrap-secrets.json` alongside
the database in consistent offline volume backups; the generated encryption key
is essential to recovering a raw database copy. It is deliberately excluded
from portable encrypted application exports. See [Portainer persistence](PORTAINER.md#persistence-backups-and-upgrades).
See [installation and recovery](installation-and-recovery.md) for the encrypted
admin backup/export flow and its size limits. It supports configuration,
database and optional artwork cache. The backup passphrase cannot be recovered.
Keep it separately and test a restore to a disposable instance of the same
version.
For an offline volume backup, stop **only Magent** and snapshot/copy its complete
data volume, including SQLite journal/WAL sidecars. Start it again after the
consistent copy completes. Keep `.env` and the original encryption/signing keys
separately protected: encrypted settings in a raw database copy cannot be
recovered without the original encryption key (or original signing key for
older installations that derived their encryption key from it). The portable
encrypted application backup re-encrypts settings for its destination instead;
these are different recovery procedures.
Managed portable restore keeps the destination's confirmed application URL as
well as its signing/encryption keys. It does not replace the destination's
trusted origin with the source backup's URL; review integration addresses and
sign in using the destination URL after recovery.
Never rotate keys, replace the database or delete volumes as a response to an
unhealthy container. Inspect health/log errors without posting credentials.
## Public release gates
The release branch is a source snapshot, not a published image. Build and
verify the exact commit before publishing Docker Hub tags.
This guide and the lightweight Dockerfile do not by themselves certify a
release. Before publishing a new immutable tag or moving `latest`:
- Build from the reviewed release commit with the root Dockerfile and its pinned
base digests. Do not send `.env`, live data, local caches or development
credentials in the build context. Record source revision, final image digest,
compressed download size and unpacked image size separately.
- Run backend and frontend checks, the production build and a **container** smoke
test using the read-only Compose settings, a fresh named volume and non-root
user. Confirm setup survives recreation, login works and the UI hydrates
without CSP errors. Test backup/restore and an upgrade against a disposable
copy, preserving its keys; never use live accounts/data for destructive tests.
- Check same-origin POST protection: empty-form requests to both
`/api/auth/login` and `/api/auth/jellyfin/login` with the configured origin must
reach validation (422); an unrelated origin must be rejected (403). These are
necessary checks, not substitutes for a successful authenticated login.
- Inspect the final image for development packages, build caches, unexpected
credentials, unnecessary privileged execution and known dependency/base-image
vulnerabilities. Review dependency notices and licensing before redistribution.
- Declare only architectures actually built **and smoke-tested**. A Linux/amd64
test is not an ARM64 test; an amd64 image running under emulation is not native
ARM64 validation. Do not advertise a multi-architecture release until its
manifest and each advertised platform have been verified. Docker Desktop runs
the Linux image; this is not a native Windows-container or macOS build.
- Publish migration/rollback notes, supported image tags/digests and a support
and private security-reporting route. Include the MIT license and required
dependency notices, and confirm rights to any bundled branding assets.
Publish only with the release owner's approval; building locally does not
publish or deploy an image.