feat(release): publish minimal self-contained Magent source
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
# Environment variable reference
|
||||
|
||||
The public [Portainer stack](../compose.yml) needs **no environment variables**.
|
||||
Its image supplies the runtime defaults; use the first-run setup wizard to set
|
||||
the application URL, connect services and configure notifications. The normal
|
||||
stack does not need a Dockerfile, source checkout or `.env` file.
|
||||
|
||||
This reference also covers advanced/manual deployments, compatibility aliases,
|
||||
image-build inputs and repository-only tooling. A variable being listed here
|
||||
does **not** mean that it belongs in the public Compose file.
|
||||
|
||||
## Managed installation defaults and precedence
|
||||
|
||||
- The image defaults `MAGENT_MANAGED_SECRETS` to `auto`. With no manually supplied
|
||||
`JWT_SECRET`, startup generates independent signing, encryption and setup keys
|
||||
once, then reloads them from `/app/data/bootstrap-secrets.json`. An existing
|
||||
explicit signing key selects the legacy/manual path. Do not switch an existing
|
||||
installation's key management, volume or keys just to match a fresh template.
|
||||
- Managed installations fix `SQLITE_PATH` to `/app/data/magent.db` and
|
||||
`API_DOCS_ENABLED` to `false`; these are not setup choices. Preserve the entire
|
||||
`/app/data` volume, including the private keys. Startup fails rather than
|
||||
silently replacing missing keys beside an existing database.
|
||||
- Set the browser-facing application URL in setup. Managed CORS accepts the
|
||||
configured same origin; do not configure `*` or invent an external API origin.
|
||||
Before a URL is saved, only the token-authorized first-administrator setup can
|
||||
claim the initial origin; arbitrary public requests do not establish trust.
|
||||
HTTPS is required for public hosting. A private LAN can use explicit HTTP.
|
||||
- Source defaults below describe `backend/app/config.py` before managed bootstrap
|
||||
or saved configuration is applied. Saved, supported application settings take
|
||||
precedence over their environment fallback. Security/bootstrap settings are
|
||||
deployment controls, not ordinary editable settings. Container listener ports
|
||||
are fixed by its process supervisor, not by application settings.
|
||||
- For manual deployments, environment variables are read at process startup.
|
||||
Restart/recreate after changing them. A `.env` file is loaded by the relevant
|
||||
Compose template's `env_file`, not automatically discovered by the application.
|
||||
Keep manual credentials stable across upgrades and offline restores.
|
||||
|
||||
Defaults use JSON notation: `null` means unset, `""` means an empty string,
|
||||
`true`/`false` are booleans, and `@BUILD_NUMBER`/`@CHANGELOG` are bundled build
|
||||
metadata. Do not literally enter `null` or the `@...` labels into Portainer.
|
||||
Aliases in one row refer to the same setting; if multiple aliases are present,
|
||||
the first listed alias wins. Use only one. Never place secrets into browser-
|
||||
visible `NEXT_PUBLIC_*` variables, URLs, screenshots or public support reports.
|
||||
|
||||
## Core, authentication and storage
|
||||
|
||||
| Variable / aliases | Source default | Purpose and managed-install behaviour |
|
||||
| --- | --- | --- |
|
||||
| `APP_NAME` | `"Magent"` | Backend application name. |
|
||||
| `CORS_ALLOW_ORIGIN` | `"http://localhost:3000"` | Legacy exact allowed browser origin. Managed installs use the URL confirmed in setup automatically; no Compose override needed. |
|
||||
| `SQLITE_PATH` | `"data/magent.db"` | SQLite database file. Managed container path is fixed to `/app/data/magent.db`; manual source deployments retain their existing path. |
|
||||
| `SQLITE_JOURNAL_MODE` | `"DELETE"` | SQLite journal mode; retain the default unless deliberately configuring storage behaviour. |
|
||||
| `JWT_SECRET` | `""` | Secret signing key. Generated/persisted in managed mode; manual mode requires a strong non-default value of at least 32 characters. |
|
||||
| `JWT_EXP_MINUTES` | `120` | Authentication token lifetime in minutes. |
|
||||
| `JWT_ISSUER` | `"magent"` | Expected JWT issuer. Changing it invalidates existing tokens. |
|
||||
| `JWT_AUDIENCE` | `"magent-web"` | Expected JWT audience. Changing it invalidates existing tokens. |
|
||||
| `SETTINGS_ENCRYPTION_KEY` | `null` | Secret Fernet key for stored integration credentials. Generated/persisted in managed mode. Preserve an existing manual key (or the existing legacy signing-key-derived configuration). |
|
||||
| `API_DOCS_ENABLED` | `false` | OpenAPI/interactive API documentation. Forced off in managed mode; leave off for public deployment. |
|
||||
| `AUTH_RATE_LIMIT_WINDOW_SECONDS` | `60` | Login rate-limit window in seconds. |
|
||||
| `AUTH_RATE_LIMIT_MAX_ATTEMPTS_IP` | `15` | Login attempts allowed per client IP per window. |
|
||||
| `AUTH_RATE_LIMIT_MAX_ATTEMPTS_USER` | `5` | Login attempts allowed per user identifier per window. |
|
||||
| `PASSWORD_RESET_RATE_LIMIT_WINDOW_SECONDS` | `300` | Password-reset rate-limit window in seconds. |
|
||||
| `PASSWORD_RESET_RATE_LIMIT_MAX_ATTEMPTS_IP` | `6` | Password-reset attempts allowed per IP per window. |
|
||||
| `PASSWORD_RESET_RATE_LIMIT_MAX_ATTEMPTS_IDENTIFIER` | `3` | Password-reset attempts allowed per account identifier per window. |
|
||||
| `ADMIN_USERNAME` | `"admin"` | Legacy bootstrap username when supplying an initial administrator password. Wizard-created administrators choose their own name. |
|
||||
| `ADMIN_PASSWORD` | `""` | Optional manual first-administrator password, minimum 12 characters. Leave empty for token-protected setup; never use a shared default password. |
|
||||
| `SETUP_TOKEN` | `""` | Secret one-time administrator-creation credential. Generated in managed mode; retrieve using `python -m app.container_bootstrap setup-token` in the container console. Not shown once the first administrator exists/setup is complete. |
|
||||
| `AUTH_COOKIE_NAME` | `"magent_auth"` | HttpOnly session-cookie name. |
|
||||
| `AUTH_COOKIE_SECURE` | `false` | Legacy cookie Secure flag; public HTTPS deployments require `true`. Managed mode derives the correct behaviour from its confirmed application URL. |
|
||||
| `AUTH_COOKIE_SAMESITE` | `"strict"` | Authentication cookie SameSite policy; retain `strict` unless deliberately evaluating a different deployment model. |
|
||||
| `AUTH_COOKIE_DOMAIN` | `null` | Cookie domain override; unset creates safer host-only cookies. |
|
||||
| `AUTH_STATE_COOKIE_NAME` | `"magent_logged_in"` | Non-secret UI login-state marker. The frontend expects its normal default. |
|
||||
|
||||
## Logs, request cache and issue follow-up
|
||||
|
||||
| Variable / aliases | Source default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | `"INFO"` | Application log verbosity. |
|
||||
| `LOG_FORMAT` | `"text"` | `text` for readable logs or `json` for structured collection. |
|
||||
| `LOG_FILE` | `"data/magent.log"` | Rotating application log; resolves to `/app/data/magent.log` in the combined image. |
|
||||
| `LOG_FILE_MAX_BYTES` | `20000000` | Maximum active log size before rotation, in bytes. |
|
||||
| `LOG_FILE_BACKUP_COUNT` | `10` | Number of rotated log files to keep. |
|
||||
| `LOG_HTTP_CLIENT_LEVEL` | `"INFO"` | Outbound integration HTTP logging verbosity. |
|
||||
| `LOG_BACKGROUND_SYNC_LEVEL` | `"INFO"` | Scheduled background-sync log verbosity. |
|
||||
| `REQUESTS_SYNC_TTL_MINUTES` | `1440` | Request-cache freshness period in minutes. |
|
||||
| `REQUESTS_STAGE_REFRESH_MINUTES` | `15` | Background request-stage refresh interval, from 1 to 1440 minutes. Short intervals increase integration traffic. |
|
||||
| `REQUESTS_POLL_INTERVAL_SECONDS` | `300` | Interval for checking whether a full request sync is due. |
|
||||
| `REQUESTS_DELTA_SYNC_INTERVAL_MINUTES` | `5` | Incremental new/changed request polling interval. |
|
||||
| `REQUESTS_FULL_SYNC_TIME` | `"00:00"` | Daily full request-cache rebuild time, `HH:MM`. |
|
||||
| `REQUESTS_CLEANUP_TIME` | `"02:00"` | Daily request-history cleanup time, `HH:MM`. |
|
||||
| `REQUESTS_CLEANUP_DAYS` | `90` | Request-history retention period in days. |
|
||||
| `REQUESTS_DATA_SOURCE` | `"prefer_cache"` | Request data-source strategy; prefer cached results by default. |
|
||||
| `ISSUE_CONFIRMATION_CONTACT_ATTEMPTS` | `2` | Confirmation emails after an issue is fixed; `0` closes without sending these emails. |
|
||||
| `ISSUE_CONFIRMATION_INTERVAL_VALUE` | `3` | Time between confirmation attempts and final closure wait, in the unit below. |
|
||||
| `ISSUE_CONFIRMATION_INTERVAL_UNIT` | `"days"` | Follow-up interval unit: `days`, `weeks` or `months`. |
|
||||
| `ARTWORK_CACHE_MODE` | `"remote"` | Artwork delivery mode; remote sources by default, local caching when configured. |
|
||||
|
||||
## Site appearance and login
|
||||
|
||||
| Variable / aliases | Source default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `SITE_BUILD_NUMBER` | `@BUILD_NUMBER` | Bundled release build number; normally leave unchanged. |
|
||||
| `SITE_CHANGELOG` | `@CHANGELOG` | Bundled release changelog; normally leave unchanged. |
|
||||
| `SITE_BANNER_ENABLED` | `false` | Enable the signed-in sitewide announcement banner. |
|
||||
| `SITE_BANNER_MESSAGE` | `null` | Banner message. |
|
||||
| `SITE_BANNER_TONE` | `"info"` | Banner preset tone when custom colours are unset. |
|
||||
| `SITE_BANNER_BACKGROUND_COLOR` | `null` | Optional six-digit hexadecimal banner background colour, e.g. `#123456`. |
|
||||
| `SITE_BANNER_BORDER_COLOR` | `null` | Optional six-digit hexadecimal banner border colour. |
|
||||
| `SITE_LOGIN_MESSAGE` | `null` | Separate message on the logged-out login page. |
|
||||
| `SITE_LOGIN_SHOW_JELLYFIN_LOGIN` | `true` | Show the Jellyfin login option. |
|
||||
| `SITE_LOGIN_SHOW_LOCAL_LOGIN` | `true` | Show local Magent login. |
|
||||
| `SITE_LOGIN_SHOW_FORGOT_PASSWORD` | `true` | Show password recovery. |
|
||||
| `SITE_LOGIN_SHOW_SIGNUP_LINK` | `true` | Show invite signup. |
|
||||
| `SITE_NAV_SHOW_REQUESTS` | `true` | Show requests navigation. |
|
||||
|
||||
## Application URLs, proxy and TLS
|
||||
|
||||
Configure these supported settings in setup/admin rather than adding environment
|
||||
entries to the public stack. Do not assume these settings reconfigure Docker
|
||||
port mappings or the bundled supervisor: it listens on frontend `3000` and
|
||||
backend `8000`, with only `3000` published. Terminate public HTTPS at a reverse
|
||||
proxy. Only explicitly trusted proxy addresses may supply forwarded headers.
|
||||
|
||||
| Variable / aliases | Source default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `MAGENT_APPLICATION_URL` | `null` | Canonical browser-facing origin used by links and request-origin protection. Confirm it in setup; no initial environment value is required in managed mode. |
|
||||
| `MAGENT_APPLICATION_PORT` | `3000` | Preferred application port metadata for direct/local deployments; does not change the image listener or host port mapping. |
|
||||
| `MAGENT_API_URL` | `null` | Optional canonical API URL metadata. Normal users access same-origin `/api`; do not expose backend port 8000 publicly. |
|
||||
| `MAGENT_API_PORT` | `8000` | Preferred backend port metadata; does not change the image listener. |
|
||||
| `MAGENT_BIND_HOST` | `"0.0.0.0"` | Direct/local-hosting bind configuration; container supervisor retains its configured listeners. |
|
||||
| `MAGENT_PROXY_ENABLED` | `false` | Enable configured proxy-aware URL handling. |
|
||||
| `MAGENT_PROXY_BASE_URL` | `null` | Optional configured reverse-proxy public base URL. |
|
||||
| `MAGENT_PROXY_TRUST_FORWARDED_HEADERS` | `true` | Permit forwarding metadata only from trusted proxies. |
|
||||
| `MAGENT_PROXY_TRUSTED_PROXIES` | `"127.0.0.1,::1"` | Comma-separated trusted proxy addresses/networks; loopback by default. Do not broadly trust arbitrary clients. |
|
||||
| `MAGENT_PROXY_FORWARDED_PREFIX` | `null` | Optional reverse-proxy path prefix metadata. This is not a promise that every root-based frontend asset supports arbitrary subpaths. |
|
||||
| `MAGENT_SSL_BIND_ENABLED` | `false` | Direct-hosting TLS configuration; does not replace the bundled image's external HTTPS proxy. |
|
||||
| `MAGENT_SSL_CERTIFICATE_PATH` | `null` | Direct-hosting TLS certificate PEM path. |
|
||||
| `MAGENT_SSL_PRIVATE_KEY_PATH` | `null` | Secret direct-hosting TLS private-key PEM path. |
|
||||
| `MAGENT_SSL_CERTIFICATE_PEM` | `null` | Direct-hosting certificate PEM content. |
|
||||
| `MAGENT_SSL_PRIVATE_KEY_PEM` | `null` | Secret direct-hosting private-key PEM content. |
|
||||
|
||||
## Notification services
|
||||
|
||||
Provider credentials are secrets. Configure them in the authenticated setup/admin
|
||||
UI, where sensitive saved values are encrypted; do not share webhook URLs or bot
|
||||
tokens in support logs. The master switch and each provider switch both apply.
|
||||
|
||||
| Variable / aliases | Source default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `MAGENT_NOTIFY_ENABLED` | `false` | Master notification switch. |
|
||||
| `MAGENT_NOTIFY_EMAIL_ENABLED` | `false` | Enable SMTP email notifications. |
|
||||
| `MAGENT_NOTIFY_EMAIL_SMTP_HOST` | `null` | SMTP hostname/address. |
|
||||
| `MAGENT_NOTIFY_EMAIL_SMTP_PORT` | `587` | SMTP port, commonly 587 with STARTTLS or 465 with implicit TLS. |
|
||||
| `MAGENT_NOTIFY_EMAIL_SMTP_USERNAME` | `null` | SMTP login username. |
|
||||
| `MAGENT_NOTIFY_EMAIL_SMTP_PASSWORD` | `null` | Secret SMTP password/app password. |
|
||||
| `MAGENT_NOTIFY_EMAIL_FROM_ADDRESS` | `null` | Sender email address. |
|
||||
| `MAGENT_NOTIFY_EMAIL_FROM_NAME` | `null` | Sender display name. |
|
||||
| `MAGENT_NOTIFY_EMAIL_USE_TLS` | `true` | Use SMTP STARTTLS. |
|
||||
| `MAGENT_NOTIFY_EMAIL_USE_SSL` | `false` | Use implicit SMTP TLS instead of STARTTLS. |
|
||||
| `MAGENT_NOTIFY_DISCORD_ENABLED` | `false` | Enable Discord notifications. |
|
||||
| `MAGENT_NOTIFY_DISCORD_WEBHOOK_URL` | `null` | Secret Discord webhook URL, also usable for feedback routing. |
|
||||
| `MAGENT_NOTIFY_TELEGRAM_ENABLED` | `false` | Enable Telegram notifications. |
|
||||
| `MAGENT_NOTIFY_TELEGRAM_BOT_TOKEN` | `null` | Secret Telegram bot token. |
|
||||
| `MAGENT_NOTIFY_TELEGRAM_CHAT_ID` | `null` | Telegram destination chat/group/user ID. |
|
||||
| `MAGENT_NOTIFY_PUSH_ENABLED` | `false` | Enable push-provider notifications. |
|
||||
| `MAGENT_NOTIFY_PUSH_PROVIDER` | `"ntfy"` | Provider selector such as `ntfy`, `gotify`, `pushover` or `webhook`. |
|
||||
| `MAGENT_NOTIFY_PUSH_BASE_URL` | `null` | Push-service base URL. |
|
||||
| `MAGENT_NOTIFY_PUSH_TOPIC` | `null` | Push topic/channel name. |
|
||||
| `MAGENT_NOTIFY_PUSH_TOKEN` | `null` | Secret push-service token/API key. |
|
||||
| `MAGENT_NOTIFY_PUSH_USER_KEY` | `null` | Provider recipient key, such as a Pushover user key. |
|
||||
| `MAGENT_NOTIFY_PUSH_DEVICE` | `null` | Optional target device selector. |
|
||||
| `MAGENT_NOTIFY_WEBHOOK_ENABLED` | `false` | Enable generic webhook notifications. |
|
||||
| `MAGENT_NOTIFY_WEBHOOK_URL` | `null` | Generic notification webhook URL; may contain secret credentials. |
|
||||
| `MAGENT_ALLOW_PRIVATE_NOTIFICATION_TARGETS` | `false` | Explicitly allow private-network notification destinations. Keep disabled unless deliberately trusting an internal endpoint. |
|
||||
| `DISCORD_WEBHOOK_URL` | `null` | Legacy Discord feedback webhook fallback. Prefer the notification provider setting above. |
|
||||
|
||||
## Media integrations
|
||||
|
||||
URLs must be reachable from the Magent container. `localhost` names the Magent
|
||||
container itself, not another application or the Docker host. Configure these
|
||||
through setup/admin; API keys and passwords are secret. Aliases are retained for
|
||||
existing installations.
|
||||
|
||||
| Variable / aliases | Source default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `JELLYSEERR_URL`, `JELLYSEERR_BASE_URL` | `null` | Seerr/Jellyseerr request-service base URL. |
|
||||
| `JELLYSEERR_API_KEY`, `JELLYSEERR_KEY` | `null` | Seerr API key. |
|
||||
| `JELLYSTAT_URL`, `JELLYSTAT_BASE_URL` | `null` | Jellystat statistics-service base URL, including its base path if used. |
|
||||
| `JELLYSTAT_API_KEY` | `null` | Jellystat API key. |
|
||||
| `JELLYFIN_URL`, `JELLYFIN_BASE_URL` | `null` | Jellyfin server URL for authentication, user sync and library lookups. |
|
||||
| `JELLYFIN_API_KEY`, `JELLYFIN_KEY` | `null` | Jellyfin administrative API key. |
|
||||
| `JELLYFIN_PUBLIC_URL` | `null` | Browser-facing Jellyfin URL for watch/open buttons. |
|
||||
| `JELLYFIN_SYNC_TO_ARR` | `true` | Automatically add existing Jellyfin items to Sonarr/Radarr tracking where supported. |
|
||||
| `SONARR_URL`, `SONARR_BASE_URL` | `null` | Sonarr TV-service base URL. |
|
||||
| `SONARR_API_KEY`, `SONARR_KEY` | `null` | Sonarr API key. |
|
||||
| `SONARR_QUALITY_PROFILE_ID` | `null` | Default Sonarr quality profile; requests use Seerr's default if Magent has none configured. |
|
||||
| `SONARR_ROOT_FOLDER` | `null` | Sonarr TV root folder. |
|
||||
| `SONARR_QBITTORRENT_CATEGORY` | `"sonarr"` | Legacy qBittorrent category setting, retained for compatibility and hidden from the ordinary settings UI. |
|
||||
| `RADARR_URL`, `RADARR_BASE_URL` | `null` | Radarr movie-service base URL. |
|
||||
| `RADARR_API_KEY`, `RADARR_KEY` | `null` | Radarr API key. |
|
||||
| `RADARR_QUALITY_PROFILE_ID` | `null` | Default Radarr quality profile; requests use Seerr's default if Magent has none configured. |
|
||||
| `RADARR_ROOT_FOLDER` | `null` | Radarr movie root folder. |
|
||||
| `RADARR_QBITTORRENT_CATEGORY` | `"radarr"` | Legacy qBittorrent category setting, retained for compatibility and hidden from the ordinary settings UI. |
|
||||
| `BAZARR_URL`, `BAZARR_BASE_URL` | `null` | Bazarr subtitle-service base URL. |
|
||||
| `BAZARR_API_KEY`, `BAZARR_KEY` | `null` | Bazarr API key. |
|
||||
| `BAZARR_DEFAULT_LANGUAGE` | `"en"` | Default subtitle search language code. |
|
||||
| `PROWLARR_URL`, `PROWLARR_BASE_URL` | `null` | Prowlarr indexer-service base URL. |
|
||||
| `PROWLARR_API_KEY`, `PROWLARR_KEY` | `null` | Prowlarr API key. |
|
||||
| `QBIT_URL`, `QBITTORRENT_URL`, `QBITTORRENT_BASE_URL` | `null` | qBittorrent base URL for download status. |
|
||||
| `QBIT_USERNAME`, `QBITTORRENT_USERNAME` | `null` | qBittorrent login username. |
|
||||
| `QBIT_PASSWORD`, `QBITTORRENT_PASSWORD` | `null` | Secret qBittorrent password. |
|
||||
|
||||
## Additional runtime and image controls
|
||||
|
||||
These variables are read outside `Settings`. Defaults below identify their actual
|
||||
consumer; builder values and supervisor values are not user-editable app settings.
|
||||
|
||||
| Variable | Default / scope | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `MAGENT_MANAGED_SECRETS` | Image: `auto` | Automatically use persistent generated secrets when no manual signing key is supplied. `true` explicitly opts in; `false` retains manual management. Existing keys must be preserved. |
|
||||
| `MAGENT_RUNTIME_MANAGED` | Internal bootstrap output: `1` for managed mode | Internal marker set by bootstrap for backend/frontend origin and cookie handling. It is not a supported user override; do not set it in Compose. |
|
||||
| `BACKGROUND_TASKS_ENABLED` | `true` | Run background schedulers/workers. `false` is useful for isolated tests, not normal service operation. |
|
||||
| `BRANDING_SOURCE` | `bundled` | Use bundled logo/favicon; `data` prefers custom assets under persistent data. |
|
||||
| `MAGENT_METRICS_ENABLED` | Disabled (empty) | `true` enables the separate Prometheus metrics listener. Do not expose it publicly. |
|
||||
| `MAGENT_METRICS_PORT` | `9108` | Metrics listener port when enabled. |
|
||||
| `MAGENT_METRICS_BIND` | `127.0.0.1` | Metrics listener bind address when enabled. |
|
||||
| `MAGENT_COMING_SOON` | Disabled (unset) | Frontend root-route holding page enabled only by the exact value `true`. |
|
||||
| `BACKEND_INTERNAL_URL` | Build/supervisor: `http://127.0.0.1:8000`; source fallback: `http://backend:8000` | Next.js internal API/branding rewrite destination, baked when building its configuration. Runtime overrides do not rebuild a published image's rewrites. |
|
||||
| `NEXT_PUBLIC_API_BASE` | `/api` | Browser API prefix. Public frontend values are baked at build time; never place credentials here. |
|
||||
| `NODE_ENV` | Image: `production` | Node/Next runtime mode. Development allows development-only CSP eval; do not override in public production. |
|
||||
| `NEXT_TELEMETRY_DISABLED` | Image/builder: `1` | Disable Next.js telemetry. |
|
||||
| `HOSTNAME` | Supervisor: `0.0.0.0` | Bundled standalone frontend listen address, explicitly set by supervisor. |
|
||||
| `PORT` | Supervisor: `3000` | Bundled standalone frontend port, explicitly set by supervisor. |
|
||||
| `PYTHONDONTWRITEBYTECODE` | Image: `1` | Do not write Python bytecode into the read-only image. |
|
||||
| `PYTHONUNBUFFERED` | Image: `1` | Flush Python stdout/stderr without buffering. |
|
||||
| `MAGENT_UID` | Build argument: `1000` | Image runtime user's UID. Not a runtime environment switch; rebuilding with a different UID also requires matching volume/tmpfs ownership. |
|
||||
| `MAGENT_GID` | Build argument: `1000` | Image runtime user's GID; same ownership caveat as UID. |
|
||||
|
||||
## Manual Compose and test tooling
|
||||
|
||||
These optional variables belong to the advanced manual template or isolated
|
||||
verification tools, not the zero-input public Compose install.
|
||||
|
||||
| Variable | Default / scope | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `MAGENT_IMAGE` | Required by `docker-compose.hub.yml` | Explicit published image tag/digest for manual-secret installations. |
|
||||
| `MAGENT_BIND_ADDRESS` | `127.0.0.1` | Manual template frontend bind address. |
|
||||
| `MAGENT_HTTP_PORT` | `3000` | Manual template frontend host port. |
|
||||
| `PYTHON_BIN` | `python3` | Interpreter used by the backend quality gate. |
|
||||
| `MAGENT_IMAGE_MAX_MB` | `350` | Container smoke-test unpacked image size budget in MiB. |
|
||||
| `MAGENT_SMOKE_MANAGED` | `false` | Test generated managed secrets when true, or synthetic manual keys when false. |
|
||||
| `GITHUB_RUN_ID` | `local` | Optional CI identifier for disposable smoke-test resources. |
|
||||
|
||||
## Keeping this reference complete
|
||||
|
||||
Run `python scripts/check_environment_docs.py`. The dependency-free check parses
|
||||
the Settings AST (including every alias and source default), scans explicit
|
||||
runtime environment reads, Docker build/runtime declarations, Compose variables
|
||||
and repository tooling. It never imports application settings, reads `.env`,
|
||||
contacts a service or prints secret values. Backend unit tests run the same check
|
||||
so newly declared variables and changed Settings defaults require documentation.
|
||||
|
||||
This is the inventory of variables explicitly consumed/declared by Magent's
|
||||
source, not an enumeration of all knobs understood by Python, Node, Docker,
|
||||
OpenSSL or third-party libraries. Unsupported third-party/internal flags should
|
||||
not be used to bypass the packaged deployment defaults.
|
||||
@@ -0,0 +1,175 @@
|
||||
# Install with Portainer
|
||||
|
||||
For a **fresh installation**, paste [compose.yml](../compose.yml) into a new
|
||||
Portainer stack and deploy with **no environment variables**. The stack names
|
||||
`image: rephl3xnz/magent:latest` directly. Portainer pulls that prebuilt Docker Hub image; Magent
|
||||
creates its private keys, database and persistent storage, then guides you
|
||||
through administrator and app setup. Users do not need a Dockerfile, source
|
||||
checkout, Python installation, `.env` file or another database container.
|
||||
|
||||
**Release availability:** the image with zero-input managed bootstrap and
|
||||
wizard-configured origins still needs publishing to Docker Hub. These repository
|
||||
changes have not published an image or changed a live deployment. Until the
|
||||
release owner updates `latest` with this release, the template may pull an older
|
||||
image that lacks these features. `latest` is mutable, not an immutable release
|
||||
identifier; record the deployed digest and review release notes before updating.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A Portainer-managed **Docker Standalone** environment running Linux containers.
|
||||
This template is for one Magent instance, not Docker Swarm or multiple replicas
|
||||
sharing SQLite.
|
||||
- A compatible published image for your CPU architecture. Only `linux/amd64`
|
||||
has been validated; do not assume ARM64 support.
|
||||
- A stable address you will actually use in your browser. Use HTTPS before
|
||||
exposing Magent publicly or inviting internet users.
|
||||
- Existing media services, if you want to connect them. This stack installs
|
||||
Magent, not Jellyfin, Seerr or the Arr applications.
|
||||
|
||||
## Deploy and finish setup
|
||||
|
||||
1. In your Docker environment, open **Stacks**, choose **Add stack**, name it
|
||||
`magent`, and select **Web editor**. Paste the complete root `compose.yml`.
|
||||
Uploading that file is an alternative. See
|
||||
[Portainer's stack instructions](https://docs.portainer.io/user/docker/stacks/add).
|
||||
2. Leave the stack's **Environment variables** section empty. Keep the
|
||||
runtime-security block unchanged. No signing keys, database path, API-docs
|
||||
flag, application URL or CORS value needs entering into the stack.
|
||||
3. Choose **Deploy the stack** and wait for the Magent container to become
|
||||
healthy. The image's non-root user owns a fresh named volume automatically.
|
||||
4. Open that container's **Console**, choose command `/bin/ash` and user
|
||||
**`magent`** (UID `1000`), connect, and run:
|
||||
|
||||
```sh
|
||||
python -m app.container_bootstrap setup-token
|
||||
```
|
||||
|
||||
This deliberately displays a private first-install token only in your
|
||||
administrative console, not the normal container logs. Keep the output
|
||||
private: anyone with that token and access to an unclaimed installation can
|
||||
create its first administrator. The command refuses to reveal it once an
|
||||
administrator exists.
|
||||
5. Open Magent at the browser address you intend to use, such as
|
||||
`http://192.168.1.50:3000` on a trusted LAN or `https://magent.example.com`
|
||||
through your configured HTTPS proxy. Use the **Docker host's reachable
|
||||
address**, not Portainer's address if Portainer runs elsewhere. The fresh
|
||||
installation opens `/setup`. Confirm the application URL shown there,
|
||||
enter the token and create your administrator with a unique password. The
|
||||
confirmed URL must match the browser origin, including any non-default port,
|
||||
with no path, trailing slash, credentials or query string. To choose another
|
||||
address, open setup at that address first. Token-authorized creation saves
|
||||
the URL and administrator together; arbitrary visitors cannot claim a trusted
|
||||
origin merely by loading a page. Then connect/test the apps you use, select
|
||||
preferences and finish. Optional apps can be skipped. There is no shared
|
||||
default administrator password.
|
||||
6. Make an encrypted backup from **Settings → Advanced tools → Backup & restore**
|
||||
and save its passphrase separately. Test recovery before relying on the
|
||||
installation.
|
||||
|
||||
There is no need to manually generate keys or remove an environment setup token
|
||||
in this mode. First-admin creation stops accepting the token after an account
|
||||
has been created. Restarting or recreating the container retains the same
|
||||
database and keys; it does not reopen first-admin signup.
|
||||
Managed CORS and cookie security follow the saved URL automatically. SQLite is
|
||||
fixed at `/app/data/magent.db`, and API documentation is disabled; neither is a
|
||||
setup choice. All environment options, including advanced legacy overrides, are
|
||||
listed in the [complete environment reference](ENVIRONMENT.md).
|
||||
|
||||
App connection URLs must be reachable **from Magent's container**. `localhost`
|
||||
refers to Magent itself, not the Docker host or another application. Use LAN/DNS
|
||||
addresses or explicitly attach applications to an appropriate shared Docker
|
||||
network. Never mount the Docker socket into Magent.
|
||||
|
||||
## Ports, HTTPS and security defaults
|
||||
|
||||
The default publishes host port `3000` on all interfaces so a browser on the LAN
|
||||
can reach a typical Portainer deployment. Restrict that port with your host and
|
||||
network firewall. Do not port-forward this plain-HTTP endpoint to the internet.
|
||||
Only port 3000 is published; browser API requests use `/api` on the same origin.
|
||||
|
||||
The default port mapping is the literal `3000:3000`; this template has no variable
|
||||
substitutions. An operator needing another host port can deliberately edit only
|
||||
the left-hand port, then use/confirm that address in setup. For a reverse proxy
|
||||
running directly on the Docker host, `127.0.0.1:3000:3000` restricts the listener
|
||||
to host loopback. A containerized proxy instead needs an explicitly shared Docker
|
||||
network or reachable host interface; its own loopback is not the Docker host.
|
||||
|
||||
For public service, configure DNS and an HTTPS reverse proxy, set
|
||||
the application URL to the external `https://` origin in setup/admin.
|
||||
Managed installations derive matching CORS and Secure cookies from that saved
|
||||
URL, while an explicitly confirmed private HTTP origin permits HTTP cookies.
|
||||
They do not install a certificate or reverse proxy. See the
|
||||
[reverse-proxy guide](PUBLIC_RELEASE.md#https-public-urls-and-reverse-proxies).
|
||||
Keep the configured address consistent: visiting an IP when the configured
|
||||
origin is a domain can make sign-in fail the origin check.
|
||||
|
||||
The following Docker runtime controls stay enabled in the Compose file:
|
||||
|
||||
- `read_only: true` protects the image filesystem; only the data volume and
|
||||
designated temporary areas are writable.
|
||||
- `cap_drop: ["ALL"]` and `security_opt: ["no-new-privileges:true"]` restrict
|
||||
process privileges.
|
||||
- `init: true` handles child-process reaping and signal forwarding.
|
||||
- `tmpfs` supplies restricted temporary writable areas for process state and
|
||||
frontend cache without making the image writable.
|
||||
|
||||
**Leave this security block unchanged.** These are Docker engine settings, not
|
||||
application environment variables; deleting them does not make them implicit
|
||||
image defaults. No privileged container or Docker socket access is required.
|
||||
|
||||
Advanced/manual environment installs remain supported separately; see
|
||||
[ENVIRONMENT.md](ENVIRONMENT.md). Adding an unreferenced variable to Portainer's
|
||||
variable list alone does not inject it into this no-variable template. Do not
|
||||
add manual secrets or attempt to override fixed managed defaults in a working
|
||||
managed installation.
|
||||
|
||||
HTTP is intended for trusted-LAN setup/testing, not a fully secure deployment.
|
||||
Some browser features (such as clipboard access and report/newsletter operations
|
||||
using secure-context APIs) require HTTPS. Configure HTTPS for ongoing use.
|
||||
|
||||
## Persistence, backups and upgrades
|
||||
|
||||
Keep the stack name stable. Docker creates a project-scoped `magent-data` named
|
||||
volume mounted at `/app/data`. It holds the database, cached artwork, branding
|
||||
and `/app/data/bootstrap-secrets.json`, which contains the generated signing key,
|
||||
settings-encryption key and setup token. Its restrictive permissions do not
|
||||
protect it from the Docker host administrator: restrict Portainer/host access
|
||||
and use encrypted host storage. Treat the complete volume as sensitive.
|
||||
|
||||
Never delete that file to fix a startup or login problem. Without the original
|
||||
encryption key, a raw database copy's encrypted credentials cannot be recovered.
|
||||
Keep a secure, consistent offline backup of the **whole volume** by stopping
|
||||
only Magent while taking the copy. Preserve the generated secrets with that
|
||||
copy. A portable `.magent-backup` export is different: it excludes deployment
|
||||
keys and re-encrypts settings for the destination's keys during restore. See
|
||||
[backup and recovery](installation-and-recovery.md).
|
||||
Managed application-backup restore also retains the destination's confirmed
|
||||
application URL, not the backup source's URL. Its origin/cookie policy therefore
|
||||
continues to match the destination site. An offline full-volume restore is a
|
||||
different procedure and must preserve that volume's original keys.
|
||||
|
||||
Before updating, save the current image tag/digest, stack definition and backup.
|
||||
Update the existing stack with a fresh pull of `rephl3xnz/magent:latest`, retaining
|
||||
its name and volume. A plain container restart does not pull an updated image.
|
||||
For a controlled release or rollback, edit only the `image:` line to a compatible
|
||||
published immutable tag or digest; this is optional, not an installation input.
|
||||
Verify health, login,
|
||||
app connections and invites afterwards. Rollback may require the matching data
|
||||
backup if a migration is not backward compatible.
|
||||
|
||||
Do not remove volumes when deleting/recreating a stack unless you intend to
|
||||
erase the installation. Changing the stack name or mounting an empty volume
|
||||
does not migrate your data.
|
||||
|
||||
## Existing installations
|
||||
|
||||
This is a **fresh-install template**, not an automatic migration from manual
|
||||
secrets. Existing installations must retain their own data mount, database path,
|
||||
`JWT_SECRET`, `SETTINGS_ENCRYPTION_KEY` and deployment settings. Continue using
|
||||
your saved stack or [docker-compose.hub.yml](../docker-compose.hub.yml). Do not
|
||||
switch an existing bind mount to a fresh named volume or enable managed secrets
|
||||
to replace established keys. Follow an explicitly reviewed migration if you
|
||||
choose to change secret management later.
|
||||
|
||||
The project retains its Dockerfile for maintainers to build release images.
|
||||
End users deploying this stack do not download or run it.
|
||||
@@ -0,0 +1,263 @@
|
||||
# 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.
|
||||
|
||||
**Publication is still pending:** the new image must be published before the
|
||||
`latest` stack can supply this behaviour. Repository changes do not publish or
|
||||
deploy an image; an older `latest` image remains incompatible with this new
|
||||
workflow. `latest` is mutable, so record the resolved digest before an upgrade.
|
||||
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.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Installation, backup and recovery
|
||||
|
||||
## Fresh installation
|
||||
|
||||
For a new Portainer installation without a Dockerfile or `.env`, use the
|
||||
[single-file stack guide](PORTAINER.md). It generates persistent deployment
|
||||
secrets and provides a console-only setup-token command. The root stack pulls
|
||||
`rephl3xnz/magent:latest` with no environment inputs. Confirm the application URL
|
||||
alongside the token when creating your first administrator; managed CORS and
|
||||
cookie security follow that URL automatically. SQLite is fixed at
|
||||
`/app/data/magent.db`, and API documentation remains disabled. Leave the Compose
|
||||
runtime-security defaults unchanged. The updated image still needs publishing
|
||||
before `latest` provides this behaviour; repository changes alone do not deploy
|
||||
or publish it. Record deployed digests because `latest` is mutable.
|
||||
|
||||
The **manual-secret and source-build instructions below** remain supported for
|
||||
other deployments. All environment options and defaults are documented in
|
||||
[ENVIRONMENT.md](ENVIRONMENT.md); they are not required inputs to the managed
|
||||
Portainer template.
|
||||
|
||||
Start with `.env.example`. Generate independent random values for `JWT_SECRET` and `SETUP_TOKEN` (at least 32 characters each), plus a Fernet `SETTINGS_ENCRYPTION_KEY`. Never deploy the example placeholders. Keep the environment file private.
|
||||
|
||||
```bash
|
||||
python -c "import secrets; print(secrets.token_urlsafe(48))"
|
||||
python -c "import secrets; print(secrets.token_urlsafe(48))"
|
||||
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
||||
```
|
||||
|
||||
The first two commands produce the JWT secret and setup token respectively. The third requires the backend dependencies. Alternatively generate the Fernet key using Python's standard library: `python -c "import base64, secrets; print(base64.urlsafe_b64encode(secrets.token_bytes(32)).decode())"`.
|
||||
|
||||
For a manual-secret installation, set the correct browser-facing `CORS_ALLOW_ORIGIN`, `MAGENT_APPLICATION_URL`, cookie HTTPS settings and host paths before starting. Changing the application URL in its wizard does not replace its explicit environment CORS policy. Managed Portainer installations instead confirm their origin during token-authorized first-admin setup, and automatically follow that saved origin for CORS and cookie security. Neither mode changes reverse-proxy configuration or creates certificates.
|
||||
|
||||
After `docker compose -f compose.yml -f compose.build.yml up -d --build` for a source build, visit the frontend. A new database redirects to `/setup`:
|
||||
|
||||
1. Enter `SETUP_TOKEN` and create a local administrator with a unique password of at least 12 characters. Alternatively, set `ADMIN_USERNAME` and `ADMIN_PASSWORD` in the environment before the first start, then sign in with that account.
|
||||
2. Expand each app you use: Jellyfin, Seerr/Jellyseerr, Sonarr, Radarr, Prowlarr, qBittorrent, Bazarr and Jellystat. Enter its internal address and credentials, then **Save & test**. For Sonarr/Radarr, a successful check loads quality profiles and root folders.
|
||||
3. Set site access, request refresh/retention and optional SMTP preferences. Invite signup remains invite-only.
|
||||
4. Review and finish. Magent starts its configured background jobs, unless `BACKGROUND_TASKS_ENABLED=false`.
|
||||
|
||||
Use server-reachable addresses: `localhost` in a container refers to that container. Optional apps can be skipped. Each successful save persists; closing the tab leaves setup resumable. Unsaved form fields are not retained. Remove `SETUP_TOKEN` after finishing. Bootstrap is permanently disabled after setup, and cannot replace an existing administrator. Administrators can revisit the wizard from Settings without resetting the installation.
|
||||
|
||||
Upgrades with an existing users table are marked configured automatically. Setup status reveals only whether setup is needed and whether the first administrator is missing. Configuration and wizard progress require administrator authentication. First-admin creation uses a constant-time token comparison, persistent rate limits and a database transaction to prevent concurrent claims.
|
||||
|
||||
## Create a backup
|
||||
|
||||
Open **Settings → Advanced tools → Backup & restore** (`/admin/backups`). Choose a unique backup passphrase of 12–1024 characters, confirm it, optionally include the filesystem artwork cache, and download the `.magent-backup` file.
|
||||
|
||||
Every backup includes:
|
||||
|
||||
- A consistent SQLite snapshot: users, password hashes, invite records, requests, issues, settings, saved statistics, subscriptions and database-backed caches.
|
||||
- Portable runtime configuration, including environment-provided app credentials. Secrets are decrypted only inside the private export staging area and encrypted archive; they are re-encrypted with the destination installation key when restoring.
|
||||
- Custom branding (`data/branding/logo.png` and `favicon.ico`).
|
||||
|
||||
The optional cache adds supported TMDB artwork from `data/artwork/tmdb`. In-memory caches are rebuilt, not backed up. Media files, the connected apps' databases, log files, `.env`, TLS private keys, host paths, signing/encryption keys and deployment/network controls are not included. Keep a separate secure record of the deployment configuration and backup passphrase.
|
||||
|
||||
Backups use authenticated AES-256-GCM encryption with a per-backup salt and scrypt-derived key. The passphrase is never stored by Magent and cannot be recovered. Keep backups and their passphrases separately, off the Magent host. Treat backups as sensitive even though encrypted.
|
||||
|
||||
Current limits: **32 MiB encrypted archive**, **128 MiB expanded data**, and **20,000 entries**. These bound memory and disk use; including a large artwork cache can exceed them. Retry without artwork if necessary. For larger installations, use a separate operator-managed offline volume/database backup; this UI does not silently omit oversized data. Automatic scheduled backups and media-server backups are not part of this feature.
|
||||
|
||||
The frontend and backend accept up to 34 MiB for the whole multipart request, including the 32 MiB file. Configure any external reverse proxy's upload limit accordingly (for example `client_max_body_size 34m` in nginx); otherwise it may reject valid files before they reach Magent.
|
||||
|
||||
## Restore safely
|
||||
|
||||
1. Make a fresh backup of the destination. Stop external writes/other backend processes sharing its SQLite file. The supplied deployment uses one backend worker; do not run restore against a multi-worker/shared-database deployment.
|
||||
2. Sign in as an administrator, select a `.magent-backup`, enter its passphrase and type `RESTORE`. A fresh replacement installation must first create its temporary administrator through `/setup`; then use the **Restore it here** link before connecting apps.
|
||||
3. Upload and stage the restore. Magent checks authentication, encrypted integrity, archive paths and sizes, checksums, SQLite integrity, schema compatibility and an active restored administrator. Live data is unchanged at this point. A pending restore can be cancelled from the same page.
|
||||
4. Restart the application using your normal deployment process, for example `docker compose restart magent` for a source build, or restart the container in Portainer. The UI never restarts a server automatically.
|
||||
5. On startup, before schema initialization or workers, Magent creates a private rollback copy, replaces the database/selected assets and records the result. Failed or interrupted replacement is rolled back using a durable journal. Review the backend logs if startup stops.
|
||||
6. Sign in with an account from the restored backup, verify Settings/service checks, requests, issues and invite policy, then create a new backup. Old sessions and password-reset tokens are invalidated. Existing invite records and links are retained, with their original expiry and usage state.
|
||||
|
||||
Restore **replaces** the destination database; it does not merge changes made after the backup. After staging, pause normal usage until the restart so new writes are not mistaken for restored data. Do not change the destination encryption key between staging and restart. Restoring earlier invite state can also restore its remaining uses: review active invitations after recovery.
|
||||
|
||||
Use the same Magent version for restore, then upgrade normally. Portable settings follow the backup, but destination host identity, JWT/encryption keys, local paths, TLS/cookie/proxy controls and ports remain destination-owned. For managed installations, restore also preserves the destination's confirmed application URL rather than adopting the backup source's URL; its CORS/cookie policy therefore stays aligned with the destination site. Configure and verify that destination URL before staging a restore. Review service addresses when moving hosts. Without the optional artwork cache, database artwork flags are reset and missing artwork can be fetched again; the existing destination artwork directory is left in place.
|
||||
|
||||
## Recovery files
|
||||
|
||||
The `backups/` directory beside the configured SQLite database contains private staging, lock/journal/status files and `rollback-<id>/` copies. It is not a library of exported encrypted downloads. Rollback copies contain the old database and assets; protect the data volume with host encryption and restrictive access. Magent does not automatically delete rollback copies after success. After validating the restored installation and saving a separate backup, an operator may archive or remove the specific old rollback directories during maintenance. Never remove an active `pending/` directory or `restore-journal.json` during a restore.
|
||||
|
||||
Insufficient disk space or invalid input stops the operation rather than partially accepting a backup. Allow room for the upload, extracted staging database/assets, live data and a rollback copy. The supplied Docker image's unprivileged user must have write access to the persistent data volume. Do not delete the data volume or replace `.env` to retry setup or recovery.
|
||||
Reference in New Issue
Block a user