# Native Linux installation (no Docker) This is a manual, single-host production recipe for a Linux server with systemd. Neither Docker nor Portainer is required. For a terminal-only trial, Windows or macOS, use [foreground installation](LOCAL_DEVELOPMENT.md). See the [installation overview](INSTALLATION.md) for platform limits and networking. The operator manages OS updates, Python/Node, private keys, the reverse proxy and service lifecycle. The systemd files below are reviewable examples, not an automatic installer; adapt executable paths to your host and validate locally. ## 1. Prerequisites and layout Install **Python 3.14** with venv/pip, **Node.js 24** with npm, Git, curl and a trusted TLS reverse proxy. Follow [Python](https://www.python.org/downloads/) and [Node.js](https://nodejs.org/en/download) instructions for your OS; distribution default packages may be older. Install both runtimes at service-accessible system paths outside `/home` and `/root`; a venv linked to a user-private pyenv/uv Python can be hidden by the units' `ProtectHome` setting. Do not replace the OS's own Python. Native wheels are architecture-dependent; if pip/npm need compilation, install the appropriate compiler/library prerequisites from the dependency maintainers rather than silently changing pinned versions. Do not use `sudo pip install` into system Python. ```sh python3.14 --version python3.14 -m venv --help node --version npm --version command -v node ``` This recipe uses: | Path | Purpose | | --- | --- | | `/opt/magent` | Release source, Python venv, frontend dependencies/build | | `/etc/magent/backend.env` | Private backend keys and deployment configuration | | `/etc/magent/frontend.env` | Frontend address/bind settings; no backend secrets | | `/var/lib/magent/data` | Persistent SQLite, logs, branding, artwork and restore staging | Create a dedicated account and directories on a **fresh** host. If the account or paths already exist, inspect and reuse the intended installation; do not overwrite its configuration, clone into it or change ownership blindly. ```sh sudo useradd --system --user-group --create-home --home-dir /var/lib/magent --shell /usr/sbin/nologin magent sudo install -d -o magent -g magent -m 0755 /opt/magent sudo install -d -o root -g root -m 0700 /etc/magent sudo install -d -o magent -g magent -m 0700 /var/lib/magent/data sudo -u magent git clone --branch release --single-branch https://git.amslabs.net/Rephl3x/Magent.git /opt/magent sudo -u magent git -C /opt/magent rev-parse HEAD sudo -u magent python3.14 -m venv /opt/magent/.venv sudo -u magent /opt/magent/.venv/bin/python -m pip install -r /opt/magent/backend/requirements.txt ``` Record the checked-out commit. Use the release branch or a reviewed release commit, not the private development/deployment branches. The native API needs `backend/requirements.txt`, not Docker's Supervisor dependency file. Python virtual environments should be recreated at their final path rather than moved. See [Python venv documentation](https://docs.python.org/3.14/library/venv.html). ## 2. Create private configuration once Choose the final URL first, for example `https://magent.example.com`. DNS and TLS are configured separately in your reverse proxy. The native mode does not use `container_bootstrap` or generate keys automatically. For a **fresh installation only**, run this standard-library script to create independent keys without printing them. It refuses to overwrite an existing file. The default URL below is an example and must be edited before starting services. ```sh sudo python3.14 - <<'PY' import base64 import os from pathlib import Path import secrets path = Path('/etc/magent/backend.env') values = { 'JWT_SECRET': secrets.token_urlsafe(48), 'SETTINGS_ENCRYPTION_KEY': base64.urlsafe_b64encode(secrets.token_bytes(32)).decode(), 'SETUP_TOKEN': secrets.token_urlsafe(48), 'MAGENT_MANAGED_SECRETS': 'false', 'MAGENT_APPLICATION_URL': 'https://magent.example.com', 'CORS_ALLOW_ORIGIN': 'https://magent.example.com', 'AUTH_COOKIE_SECURE': 'true', 'API_DOCS_ENABLED': 'false', 'SQLITE_PATH': '/var/lib/magent/data/magent.db', 'LOG_FILE': '/var/lib/magent/data/magent.log', 'BRANDING_SOURCE': 'data', } fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) with os.fdopen(fd, 'w') as stream: stream.write(''.join(f'{name}={value}\n' for name, value in values.items())) print('Created private backend configuration; edit the example URL before starting.') PY sudo install -o root -g root -m 0600 /opt/magent/deploy/native/magent-frontend.env.example /etc/magent/frontend.env sudoedit /etc/magent/backend.env /etc/magent/frontend.env ``` The [backend template](../deploy/native/magent-backend.env.example) shows the same fields with placeholders for reference; never run with those placeholders. Keep both files private and keep an encrypted off-host copy of backend keys. Run the `install` command above only for a new frontend environment file; during upgrades retain your existing file rather than copying the example again. Set `MAGENT_APPLICATION_URL` in **both files**, and backend `CORS_ALLOW_ORIGIN`, to exactly the browser origin: scheme, hostname and any non-default port, without a path or trailing slash. For HTTPS set `AUTH_COOKIE_SECURE=true`; for a deliberate trusted-LAN HTTP installation set it `false` and use the explicit `http://` URL in both files. The frontend uses this to avoid inappropriate CSP HTTPS upgrades. Do not use wildcard CORS or disable CSRF protections to fix an address mismatch. Leave `ADMIN_PASSWORD` unset so the wizard creates the first administrator. Leave the internal `MAGENT_RUNTIME_MANAGED` flag unset. API docs remain disabled. All optional integration/environment controls are in [ENVIRONMENT.md](ENVIRONMENT.md); configure app credentials through the wizard instead of copying every example variable into these files. **A `.env` file is not automatically loaded by the backend.** The systemd unit loads `backend.env` explicitly. The frontend must never load that backend file or receive secrets in `NEXT_PUBLIC_*` variables. ## 3. Build the frontend Run the build as the unprivileged service account. The backend address must be present **at build time**: otherwise this project defaults to Docker's `http://backend:8000`, which does not normally resolve on a native host. ```sh sudo -u magent sh -c 'cd /opt/magent/frontend && npm ci --include=dev' sudo -u magent sh -c 'cd /opt/magent/frontend && BACKEND_INTERNAL_URL=http://127.0.0.1:8000 NEXT_PUBLIC_API_BASE=/api NEXT_TELEMETRY_DISABLED=1 NODE_ENV=production npm run build' sudo -u magent sh -c 'cd /opt/magent/frontend && cp -R public .next/standalone/ && cp -R .next/static .next/standalone/.next/' sudo -u magent mkdir -p /opt/magent/frontend/.next/standalone/.next/cache ``` Keep the build tools/dev dependencies until the build is complete. Do not serve the source with `next dev` in production. This project uses Next's **standalone** output: run its `server.js`, with the `public` and `.next/static` directories copied as above. Changing the backend host/port requires rebuilding this bundle, not merely editing its runtime environment. See [Next standalone deployment](https://nextjs.org/docs/app/api-reference/config/next-config-js/output). ## 4. Install and validate the services The supplied units run as `magent`, bind to loopback, use a read-only system view, private temporary directories and restricted writable paths. The backend has **one worker**. Its fixed working directory is important: artwork/branding use `cwd/data`, independently of `SQLITE_PATH`. Do not replace that directory with a symlink; portable backup rejects a symlinked asset root. ```sh sudo install -o root -g root -m 0644 /opt/magent/deploy/native/magent-backend.service /etc/systemd/system/magent-backend.service sudo install -o root -g root -m 0644 /opt/magent/deploy/native/magent-frontend.service /etc/systemd/system/magent-frontend.service sudoedit /etc/systemd/system/magent-frontend.service sudo systemd-analyze verify /etc/systemd/system/magent-backend.service /etc/systemd/system/magent-frontend.service sudo systemctl daemon-reload sudo systemctl enable --now magent-backend.service magent-frontend.service ``` Before verification, make frontend `ExecStart` match the system-wide Node 24 path reported by `command -v node` (`/usr/bin/node` in the template). Service managers do not inherit an interactive nvm shell; install Node at a service-accessible system path. Likewise adapt the source/venv paths if you changed this layout. Do not remove hardening settings just to mask an incorrect writable path. Inspect [systemd execution settings](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html) when adapting units to your distribution. `After` sets ordering, not application readiness: confirm the health checks below before exposing the site. ```sh sudo systemctl status magent-backend.service magent-frontend.service --no-pager curl --fail http://127.0.0.1:8000/health curl --fail http://127.0.0.1:3000/api/health curl --fail http://127.0.0.1:3000/api/setup/status sudo journalctl -u magent-backend.service -u magent-frontend.service -n 100 --no-pager ``` Both health endpoints should report `{"status":"ok"}`. A fresh setup status has `setup_required` and `needs_admin` set to `true`. Check logs locally, but sanitize them before sharing. The frontend is not a standalone static site: both processes must stay running. It is normal to see a temporary API error if the backend is still starting. ## 5. Configure HTTPS and finish setup Use the [reverse-proxy instructions](INSTALLATION.md#https-and-reverse-proxy) to send your chosen HTTPS hostname to `127.0.0.1:3000`. Keep backend port 8000 private. For a remote browser without a proxy, use an SSH tunnel for a local trial or deliberately change the frontend bind address and matching URL for trusted-LAN use; the production units intentionally are not LAN listeners by default. Retrieve only the setup token in your private administrative terminal: ```sh sudo sed -n 's/^SETUP_TOKEN=//p' /etc/magent/backend.env ``` Open the final browser URL, create the administrator, configure apps and finish the wizard. Native installations use this manually persisted token, not the container command shown in the managed-install part of the help dialog. After administrator creation, remove `SETUP_TOKEN` using `sudoedit` and restart the backend. **Do not remove/regenerate JWT_SECRET or SETTINGS_ENCRYPTION_KEY.** Restarting never reopens first-admin signup on an existing database. ```sh sudoedit /etc/magent/backend.env sudo systemctl restart magent-backend.service ``` ## Routine operation, backup and restore - Start/stop/restart with `systemctl`; enablement starts services after boot. Check the journal and `/var/lib/magent/data/magent.log` for backend errors. - Use [encrypted application backups](installation-and-recovery.md). Portable exports do not include the environment files, deployment keys or TLS keys; back those up separately and protect their passphrases. - For an offline disaster-recovery snapshot, stop both Magent services and back up `/var/lib/magent/data`, `/etc/magent` and the exact source revision/unit configuration using your trusted backup tool, then start services again. Do not copy a live SQLite file and assume it is consistent. - A staged UI restore takes effect when you restart **magent-backend.service**. Keep one worker and no other writers, keep destination keys unchanged, then verify restored accounts and integrations. Native/manual restore may import the source's saved application URL: recheck **Hosting & proxy** against your destination environment and frontend URL after recovery. ## Upgrade and roll back Plan a maintenance window; this is not a rolling multi-worker deployment. 1. Record `git -C /opt/magent rev-parse HEAD`, back up the application and private environment, and read migration/release notes. Keep the previous source and runtime dependency versions available. 2. Stop both services. Fetch `release` as the source owner and review the exact intended commit; use `git merge --ff-only origin/release` only for a clean release checkout. Do not force-reset local changes. 3. Install the new pinned backend requirements into the existing venv, or create a replacement venv at its final path if the Python version changed. Re-run **all** frontend dependency/build/static-copy steps above. Keep the same data directory and private environment; do not rerun initial key generation. 4. Review any unit changes, validate and `daemon-reload` if needed, start services and check API health, setup/login, UI assets, integrations and backups. For a rollback, stop both services, restore the recorded compatible source and dependency/build artifacts, and keep the original keys/data. If the upgrade changed the database incompatibly, use the matching pre-upgrade database backup with that older version; code-only rollback is not always safe. Portable restore should first use the same application version that created the backup. To retire a native installation, disable/stop its two units first. Retain the data and private environment until an off-host restore has been verified. Do not delete `/var/lib/magent` as a troubleshooting step.