Skip to content

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.

curl -fsSL https://install.maintenant.dev | sudo bash

Prerequisites

  • Linux (amd64 or arm64), any distribution: the binary carries its own libc
  • curl or wget
  • install (coreutils) and useradd
  • systemctl, unless installing with --no-service
  • Root access (sudo)

Installation paths

Minimal — one-liner, all defaults

curl -fsSL https://install.maintenant.dev | sudo bash

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)

curl -fsSL https://install.maintenant.dev | sudo bash -s -- --no-service

Installs the binary only. Run it manually: maintenant --addr 0.0.0.0:8080.

Pinning a specific version

MAINTENANT_VERSION=v1.6.0 curl -fsSL https://install.maintenant.dev | sudo -E bash

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:

sudo nano /etc/maintenant/maintenant.env
sudo systemctl restart maintenant

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.

curl -fsSL https://install.maintenant.dev | sudo bash

To upgrade to a specific version:

MAINTENANT_VERSION=v1.7.0 curl -fsSL https://install.maintenant.dev | sudo -E bash

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

  1. Downloads the binary and SHA256SUMS into a temp directory.
  2. Verifies the binary against its SHA256 checksum — mandatory, exits code 21 on mismatch.
  3. If cosign 3 or later is in $PATH and --skip-cosign is not set, verifies the SHA256SUMS signature from the bundle against the Sigstore transparency log, asserting that the signature was produced by the official release.yml workflow 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