Native Linux Install¶
maintenant can run as a standalone systemd service on any amd64 or arm64 Linux host: no Docker, no container runtime required.
The published binaries are statically linked against musl, so a single file per architecture runs on Debian, Ubuntu, RHEL, Rocky, Alpine or Arch. There is no libc version to match and nothing to install alongside it.
The install script handles everything: binary download, SHA256 verification, cosign signature check, system user creation, and service activation.
Prerequisites¶
- Linux (amd64 or arm64), any distribution: the binary carries its own libc
curlorwgetinstall(coreutils) anduseraddsystemctl, unless installing with--no-service- Root access (
sudo)
Installation paths¶
Minimal — one-liner, all defaults¶
Listens on 127.0.0.1:8080 by default. Use a reverse proxy (nginx, Caddy) to expose it externally.
With custom configuration¶
Pass any --flagName value after --. These are written to /etc/maintenant/maintenant.env and loaded by the systemd service.
curl -fsSL https://install.maintenant.dev | sudo bash -s -- \
--addr 0.0.0.0:8080 \
--baseUrl https://monitoring.example.com \
--organisationName "Acme Corp" \
--logLevel info
Without systemd (containers, CI, minimal hosts)¶
Installs the binary only. Run it manually: maintenant --addr 0.0.0.0:8080.
Pinning a specific version¶
The -E flag preserves the environment variable through sudo. If the version doesn't include standalone binaries (pre-v1.6.0), the script exits with code 20 and lists the 5 most recent valid versions.
Air-gapped (no outbound internet)¶
# On a machine with internet access:
curl -LO https://github.com/kOlapsis/maintenant/releases/download/v1.6.0/maintenant-v1.6.0-linux-amd64
curl -LO https://github.com/kOlapsis/maintenant/releases/download/v1.6.0/SHA256SUMS
# Transfer both files to the target host, then:
sudo bash -s -- --no-service --skip-cosign
# or install the binary manually:
sudo install -m 0755 maintenant-v1.6.0-linux-amd64 /usr/local/bin/maintenant
Configuration reference¶
Every MAINTENANT_* environment variable has a --flagName CLI equivalent. Precedence: CLI flag > environment variable > built-in default.
| CLI flag | Environment variable | Type | Default |
|---|---|---|---|
--addr |
MAINTENANT_ADDR |
string | 127.0.0.1:8080 |
--baseUrl |
MAINTENANT_BASE_URL |
string | http://<addr> |
--corsOrigins |
MAINTENANT_CORS_ORIGINS |
string | (empty = same-origin) |
--db |
MAINTENANT_DB |
string | ./maintenant.db |
--organisationName |
MAINTENANT_ORGANISATION_NAME |
string | Maintenant |
--runtime |
MAINTENANT_RUNTIME |
string | (autodetect) |
--logLevel |
MAINTENANT_LOG_LEVEL |
string | info |
--maxBodySize |
MAINTENANT_MAX_BODY_SIZE |
int | 1048576 |
--updateInterval |
MAINTENANT_UPDATE_INTERVAL |
duration | 24h |
--securityScoreThreshold |
MAINTENANT_SECURITY_SCORE_THRESHOLD |
int | (unset) |
--disableTelemetry |
MAINTENANT_DISABLE_TELEMETRY |
bool | false |
--allowPrivateWebhooks |
MAINTENANT_ALLOW_PRIVATE_WEBHOOKS |
bool | false |
--licenseKey |
MAINTENANT_LICENSE_KEY |
string | (unset) |
--smtpHost |
MAINTENANT_SMTP_HOST |
string | (unset) |
--smtpPort |
MAINTENANT_SMTP_PORT |
string | 587 |
--smtpUsername |
MAINTENANT_SMTP_USERNAME |
string | (unset) |
--smtpPassword |
MAINTENANT_SMTP_PASSWORD |
string | (unset) |
--smtpFrom |
MAINTENANT_SMTP_FROM |
string | maintenant@localhost |
--mcp |
MAINTENANT_MCP |
bool | false |
--mcpClientId |
MAINTENANT_MCP_CLIENT_ID |
string | (unset) |
--mcpClientSecret |
MAINTENANT_MCP_CLIENT_SECRET |
string | (unset) |
--mcpAllowedRedirectUris |
MAINTENANT_MCP_ALLOWED_REDIRECT_URIS |
string | (unset) |
--mcpAllowUnauthenticated |
MAINTENANT_MCP_ALLOW_UNAUTHENTICATED |
bool | false |
--k8sNamespaces |
MAINTENANT_K8S_NAMESPACES |
string | (empty = all) |
--k8sExcludeNamespaces |
MAINTENANT_K8S_EXCLUDE_NAMESPACES |
string | (unset) |
--statusUrl |
MAINTENANT_STATUS_URL |
string | (unset) |
--retentionSnapshots |
MAINTENANT_RETENTION_SNAPSHOTS |
duration | 48h |
--retentionInterval |
MAINTENANT_RETENTION_INTERVAL |
duration | 1h |
--retentionBatchSize |
MAINTENANT_RETENTION_BATCH_SIZE |
int | 1000 |
--mode |
MAINTENANT_MODE |
string | embedded |
--server |
MAINTENANT_SERVER |
string | (unset) |
--enrollment-token |
MAINTENANT_ENROLLMENT_TOKEN |
string | (unset) |
--label |
MAINTENANT_LABEL |
string | (unset) |
--grpc-listen |
MAINTENANT_GRPC_LISTEN |
string | 127.0.0.1:8443 |
--grpc-url |
MAINTENANT_GRPC_URL |
string | (unset) |
--grpc-tls-cert |
MAINTENANT_GRPC_TLS_CERT |
string | (unset) |
--grpc-tls-key |
MAINTENANT_GRPC_TLS_KEY |
string | (unset) |
--grpc-tls-insecure |
MAINTENANT_GRPC_TLS_INSECURE |
bool | false |
--grpc-insecure-skip-tls-verify |
MAINTENANT_GRPC_INSECURE_SKIP_TLS_VERIFY |
bool | false |
--agentRateLimitPerSecond |
MAINTENANT_AGENT_RATE_LIMIT_PER_SECOND |
int | 1000 |
--agentStaleThresholdSeconds |
MAINTENANT_AGENT_STALE_THRESHOLD_SECONDS |
int | 60 |
--data-dir |
MAINTENANT_DATA_DIR |
string | /var/lib/maintenant |
--embedded-agent |
MAINTENANT_EMBEDDED_AGENT |
bool | false |
--ca-cert |
MAINTENANT_CA_CERT |
string | (unset) |
--database-url |
MAINTENANT_DATABASE_URL |
string | (empty = SQLite) |
The multi-host flags keep their kebab-case spelling: they shipped before the flag registry existed and are baked into running agents and systemd units.
Installing a native agent¶
A host that only reports to an existing server installs the same binary in agent mode:
curl -fsSL https://install.maintenant.dev | sudo sh -s -- \
--mode agent \
--server grpcs://maintenant.example.com:8443 \
--enrollment-token TOKEN \
--label web-01
Run maintenant --help to see this list with descriptions at any time.
The /etc/maintenant/maintenant.env file¶
When you pass configuration flags to the install script, they are written to /etc/maintenant/maintenant.env in KEY=value format. The systemd service loads this file via EnvironmentFile=. You can edit it directly:
Re-running the script with new flags merges them into the existing file — keys you don't mention are preserved.
Upgrade¶
Re-run the install script. It overwrites the binary atomically, updates the service file if changed, and restarts the service.
To upgrade to a specific version:
Uninstall¶
# Remove binary and service, keep data and config
curl -fsSL https://install.maintenant.dev | sudo bash -s -- --uninstall
# Remove everything including /var/lib/maintenant and /etc/maintenant
curl -fsSL https://install.maintenant.dev | sudo bash -s -- --uninstall --purge
--purge prompts for confirmation if run interactively. In a non-interactive pipe (curl | bash), it executes directly — only pass --purge when you mean it.
Supply-chain verification¶
Every release includes:
| Asset | Purpose |
|---|---|
maintenant-vX.Y.Z-linux-amd64 / -arm64 |
Binary for each architecture |
install.sh |
The install script itself, stamped with the release tag |
SHA256SUMS |
Checksums for the binaries and the script |
SHA256SUMS.bundle |
Sigstore bundle: the cosign signature and its certificate |
provenance.intoto.jsonl |
SLSA Build L3 attestation |
The script published with a release carries that release's tag in its header, and
writes it into /etc/maintenant/maintenant.env (# Script version:), so a file on
disk always names the script that produced it.
What the script does automatically¶
- Downloads the binary and
SHA256SUMSinto a temp directory. - Verifies the binary against its SHA256 checksum — mandatory, exits code 21 on mismatch.
- If
cosign3 or later is in$PATHand--skip-cosignis not set, verifies theSHA256SUMSsignature from the bundle against the Sigstore transparency log, asserting that the signature was produced by the officialrelease.ymlworkflow on the correct tag.
cosign 3 is required. sign-blob dropped --output-signature and --output-certificate, so a release carries a bundle and nothing else, and reading that bundle needs a 3.x binary. An older cosign is treated like an absent one: the script says so and continues, rather than reporting a signature failure that would say nothing about the signature.
The cosign check is best-effort: if cosign is absent, the script warns and continues. Install it for full supply-chain protection:
# Install cosign (see https://docs.sigstore.dev/cosign/system_config/installation/)
curl -LO https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
sudo install -m 0755 cosign-linux-amd64 /usr/local/bin/cosign
Manual verification¶
VERSION=v1.6.0
ARCH=amd64
BASE=https://github.com/kOlapsis/maintenant/releases/download/${VERSION}
curl -LO ${BASE}/maintenant-${VERSION}-linux-${ARCH}
curl -LO ${BASE}/SHA256SUMS
curl -LO ${BASE}/SHA256SUMS.bundle
# SHA256
sha256sum -c SHA256SUMS --ignore-missing
# cosign (3.x)
cosign verify-blob \
--bundle SHA256SUMS.bundle \
--certificate-identity-regexp \
"^https://github\.com/kOlapsis/maintenant/\.github/workflows/release\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
SHA256SUMS
# SLSA provenance
gh attestation verify --owner kOlapsis maintenant-${VERSION}-linux-${ARCH}
Exit codes¶
| Code | Meaning |
|---|---|
0 |
Success |
2 |
Invalid argument |
10 |
Unsupported OS or architecture |
11 |
Not running as root |
12 |
Missing required tool (curl/wget, tar, useradd, systemctl) |
20 |
Version not found or predates standalone binary support |
21 |
SHA256 checksum mismatch |
22 |
cosign signature invalid |
30 |
Filesystem write error |
31 |
systemd service failed to start |