Files
Magent/docs/NATIVE_INSTALL.md
T

254 lines
13 KiB
Markdown

# 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.