264 lines
15 KiB
Markdown
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.
|