Repository navigation
cmd build
Generate
docker-compose.yml, nginx configs, and SSL certificates from.env.
nself build [flags]
nself build reads your .env cascade and generates all infrastructure configuration files: a docker-compose.yml with every enabled service, nginx reverse-proxy configs, and SSL certificates. It must be run after nself init and after any configuration change before restarting services.
The build pipeline loads configuration from .env → .env.{dev|staging|prod} → .env.secrets → .env.local (later files override earlier ones), merges plugin configurations from ~/.nself/plugins/, and applies security validation (password strength, no wildcard CORS in production, port binding checks). The result is a single docker-compose.yml that includes core services, optional services, monitoring, custom services (CS_1–CS_10), and any installed plugins.
By default, nself build is smart-cached: it compares .env modification time against docker-compose.yml and skips regeneration when nothing has changed. Use --force to override the cache.
ENV decides which of .env.dev / .env.staging / .env.prod loads as the second cascade layer. It is resolved in this order, and the build logs which source decided it:
-
Process environment — an
ENV=...already exported (by your shell, systemd, CI, etc.) always wins. -
.env's ownENV=key — when the process environment has noENVset, the build reads theENV=line directly out of the project's own.envfile (a single targeted lookup, before the rest of the cascade — whose own file selection depends on this value — runs). -
dev— the default when neither of the above sets anything.
This matters most on a server: a checked-out production project's .env normally carries ENV=prod, and no shell on the box necessarily exports ENV itself. Before this was fixed, a bare nself build on such a checkout silently fell back to dev and generated a dev-shaped compose (dev images, *.local.nself.org routes, mailpit/nself-admin wired in) instead of loading .env.prod. Setting ENV=prod in the shell (or CI) still works exactly as before and takes precedence over .env.
Since v1.2.2, nself build reads the project manifest (nself.yaml or nself.yml) and guarantees every declared plugin is wired into the generated stack. Two declaration shapes are accepted:
# flat list
plugins:
- cron
- notify
# tiered map, plus bundle expansion
bundle: nsentry
plugins:
free: [cron, notify]
pro: [ai-gateway]bundle: and bundles: expand through the canonical bundle catalog. For each declared plugin the build checks the plugin install directory, then the core-service alias (auth; storage is the free storage plugin and MinIO does not cover it), then attempts a best-effort auto-install from the registry (60 second timeout per plugin). Any plugin that still cannot be wired is reported with a per-plugin warning and a printed nself plugin install ... fix command. A declared plugin is never silently dropped.
| Env var | Default | Effect |
|---|---|---|
NSELF_AUTO_INSTALL_PLUGINS |
true |
Set false to disable auto-install (offline builds, hermetic CI) |
NSELF_PLUGIN_DIR |
~/.nself/plugins |
Override the plugin install directory (per-project plugin sets, CI) |
Projects without a manifest keep the install-then-discover flow unchanged.
Since v1.2.2, generated docker-compose.yml files contain no literal secret values. Secrets (Postgres password, Hasura admin secret, JWT configuration, MinIO credentials, SMTP password) are emitted as ${VAR} references. The interpolation values are written to .nself/compose.env with mode 0600, and nself start/stop/restart pass it to docker compose via --env-file. Editing a secret in .env and re-running nself build updates the running stack on next start. Projects built before v1.2.2 keep default .env discovery until rebuilt.
nself build warns on unrecognized variables in the .env cascade. Common app-owned variables (NODE_ENV, JWT_SECRET, LOG_LEVEL, COOKIE_SECRET, SSL_AUTO_TRUST, ENABLE_DEBUG, NSELF_PROJECT_NAME) are recognized and never warn. For project-specific variables, set ENV_ALLOWLIST to a comma-separated list of exact names or prefix patterns ending in *:
ENV_ALLOWLIST=MY_APP_TOKEN,FEATURE_*nself build automatically includes a Redis service in docker-compose.yml when any installed plugin requires it, even if REDIS_ENABLED is not set in .env.
Plugins that trigger Redis auto-enable: ai, claw, mux, cron, notify.
When auto-enable fires, the build prints:
Note: Redis auto-enabled because a BullMQ-backed plugin (cron, notify, ai, claw, or mux) was detected.
To disable auto-enable, uninstall the plugin or explicitly set REDIS_ENABLED=false and confirm you do not need the plugin's queue features. To opt in explicitly and suppress the note, set REDIS_ENABLED=true in .env.
nself build scans for v0.9 project artifacts before generating any files. Two or more detected artifacts trigger a hard error pointing to the migration guide. A single artifact produces a non-blocking warning. Use --no-migration-check in automation (CI) where you are certain no v0.9 projects exist. Use --allow-legacy only as a temporary workaround while running nself migrate. See Upgrade-From-v0.9.
nself build generates compose files that are compatible with both Docker Compose v4 and v5.
Docker Compose v5 introduced a stricter parser: it treats the top-level pids_limit service field as a canonical alias for deploy.resources.limits.pids. When both are present, v5 rejects the compose file with:
services.<name>: can't set distinct values on 'pids_limit' and 'deploy.resources.limits.pids': invalid compose project
The generator uses the deploy.resources.limits.pids form exclusively. The pids_limit top-level field is never emitted. This form is also valid on Compose v3.4+ and v4, so generated files work across all current Docker Compose versions.
Each long-running service gets a default pids limit of 100 to prevent fork-bomb attacks. Services that need more (such as postgres under high concurrency) override this in their configuration.
nself build regenerates nginx/sites/*.conf on every run. Before regenerating, it:
- Snapshots the directory's current contents to
.nself/backups/nginx-sites-<timestamp>/(keeping the last 5 snapshots). - Removes only the files it generated itself — identified by the
# Generated by nself buildmarker every generated conf carries on its first line.
Any file in nginx/sites/ without that marker (a hand-written conf, or one belonging to another project that happens to share the directory when NGINX_FRONTED_BY is unset) is left in place, and its path is printed as a warning instead of being deleted silently. This directory is never swept at all when NGINX_FRONTED_BY points this project's routes at another stack's nginx — see Config-Nginx § Fronted by Another Stack.
When SSL_MODE=local (the default), nself build generates local certificates and, historically, also added the project's domains to /etc/hosts so they resolve to 127.0.0.1. This is now gated:
-
ENV=prodalways skips it, regardless of whatBASE_DOMAINlooks like or any flag passed. This is the strongest available signal that the build is running against a live box. - A recognized local-dev domain (
localhost, or a domain ending in.local.nself.org,.localhost, or.local) is still updated automatically outside prod — no flag needed, this is the normal local dev experience. - Any other domain is left untouched unless you pass
--hoststo opt in explicitly (still subject to theENV=prodveto above).
When it does write, the build logs exactly which entries it's about to add before writing them, and first checks that /etc/hosts is actually writable — skipping with a warning (and the manual sudo nself dns-setup instructions) rather than attempting a write it already knows will fail. Use cmd-trust (nself trust dns) to manage /etc/hosts explicitly at any time, independent of nself build. With NSELF_HEADLESS=1 or CI=1 the build never trusts the CA or edits /etc/hosts; it prints the manual commands instead (see Config-Env-Vars).
nself build --plan shows what a build would change and writes nothing. It runs the real build in memory, compares the result with the files on disk and with the running containers, and prints one plan: the files that would be added, changed or removed, the host effects (certificates, /etc/hosts, trust store, plugin fragments, nginx snapshot, orphan containers) and the containers the next nself start would recreate. nself build with no flags computes the same plan first and then applies it, so what --plan showed is what is written. See Safe-On-Live for the guarantees and the confirmation rules.
| Flag | What it does |
|---|---|
--plan |
Print the plan and exit. With --json the output is one v1 envelope whose data is the change plan (plan_id, artifacts, effects, containers, destructive, requires_confirmation, empty). |
--yes |
Confirm a change on a prod-class env (ENV = prod or staging) without a prompt. |
--plan-id <id> |
Apply only if the plan still has this id. A stale id fails with E450 and writes nothing. |
--diff |
Print unified diffs of the planned changes to stderr. Env files show key names only, never values. |
Non-env diffs also redact secret-named keys, pairs and flags and their repeated values on either side of the diff. A secret flag or pair that reaches the end of a YAML block scalar line keeps its continuation lines hidden until the next flag or the end of the scalar. Secret values containing the literal text [REDACTED] receive the same protection.
A first build generates secrets, so --plan-id is refused for it (E451); confirm at the prompt or use --yes without it. A change on a prod-class env needs --yes or an interactive yes. In v1.5 mode (NSELF_V15=1) a non-interactive build without --yes is refused with E403 (exit 4). In the default v1.4 mode it prints the plan summary and a one-line notice on stderr, then proceeds as before. A dev env never asks. nself build --json without --plan applies and prints the applied plan as the envelope data.
Certificate bytes and host trust state are runtime effects outside artifact byte confirmation. A plan that generates or copies certificates, or installs the mkcert CA, refuses --plan-id with E454 before applying anything; its human plan and apply output disclose this exclusion, including on dev. Confirm at the prompt or use --yes without --plan-id, or provision certificates and host trust first. Plugin installation and removal similarly refuse --plan-id (E453): the build shows and, interactively on prod-class envs, confirms the render again after those effects. Downloaded plugin bytes are checked against the registry checksum rather than bound by the plan id; edits during that install window are reflected in the second render.
| Name | Shorthand | Type | Default | Env | Escalates to | JSON override | Description |
|---|---|---|---|---|---|---|---|
--allow-insecure |
bool |
false |
Allow insecure config (dev only) | ||||
--allow-legacy |
bool |
false |
Bypass v0.9 artifact check and proceed with WARNING (not recommended) | ||||
--check |
bool |
false |
Validate only, don't build | ||||
--debug |
bool |
false |
DEBUG |
Enable debug mode | |||
--diff |
bool |
false |
Print unified diffs of the planned changes to stderr (env files show key names only) | ||||
--force |
-f |
bool |
false |
Force rebuild all components and overwrite hand-edited generated files | |||
--hosts |
bool |
false |
Opt in to /etc/hosts management for a BASE_DOMAIN that isn't a recognized local-dev domain (localhost/.local.nself.org/.localhost/*.local). Never overrides ENV=prod, which always skips /etc/hosts. | ||||
--no-auto-redis |
bool |
false |
Disable automatic Redis enablement when a BullMQ-backed plugin is detected | ||||
--no-cache |
bool |
false |
Disable build cache | ||||
--no-migration-check |
bool |
false |
Skip v1 artifact detection (for automation/CI) | ||||
--no-monorepo |
bool |
false |
Disable automatic monorepo backend detection | ||||
--plan |
bool |
false |
Show what build would change (files, host effects, containers) and write nothing | ||||
--plan-id |
string |
"" |
Apply only if the change still matches this plan_id (from nself build --plan --json) | ||||
--profile |
string |
"" |
Service profile: curated subset of services to include in docker-compose.yml. app (default) — full service set, identical to pre-profile behaviour. ops — observability + CI server: postgres, hasura, auth, nginx, monitoring stack; excludes minio, mailpit, admin, functions, search. Overrides NSELF_PROFILE env var. Valid values: app, ops. | ||||
--quiet |
-q |
bool |
false |
Suppress non-error output (for CI use) | |||
--remove-orphans |
bool |
false |
Remove containers with no matching service in the freshly generated compose (G-014). Detection always runs; removal is opt-in. | ||||
--security-report |
bool |
false |
Generate security analysis | ||||
--verbose |
-v |
bool |
false |
Show environment cascade | |||
--write-lock |
bool |
false |
Write and keep nself.lock.yaml (pins images and plugins) even before v1.5, where the lock is always on | ||||
--yes |
bool |
false |
Confirm a change on a prod-class env (prod, staging) without a prompt | ||||
--help |
-h |
bool |
false |
Show help |
-
Side effect:
write -
Output:
document -
JSON:
envelope(data schema) - Exit codes: none declared
# Standard build
nself build
# Validate config only, don't write files
nself build --check
# Force rebuild everything, ignoring cache
nself build --force
# CI mode — quiet output
nself build -q
# Show the environment cascade as it loads
nself build --verbose
# See what a build would change, write nothing
nself build --plan
# Machine-readable plan, then apply exactly that plan on a prod-class env
nself build --plan --json
nself build --yes --plan-id <plan_id from the plan>
# Generate a security analysis report
nself build --security-report
# Rebuild for a specific environment
nself build --force --verbose- Commands — full command index
- Core-Services — what a stack is made of
Getting Started
Commands
- Commands, Overview
- Lifecycle: cmd-init · cmd-build · cmd-start · cmd-stop · cmd-restart · cmd-dev
- Monitoring: cmd-status · cmd-logs · cmd-health · cmd-urls · cmd-doctor · cmd-monitor · cmd-alerts · cmd-sentry · cmd-watchdog
- Data: cmd-db · cmd-backup · cmd-dr · cmd-queue · cmd-webhooks
- Config: cmd-config · cmd-service · cmd-env · cmd-promote
- Networking: cmd-ssl · cmd-trust · cmd-dns-setup
- Security: cmd-access · cmd-security · cmd-secrets
- Tenancy: cmd-tenant · cmd-billing
- Plugins: cmd-plugin · cmd-license · cmd-dogfood (extracted, CLI-R11) · cmd-k8s (extracted, CLI-R11) · cmd-encryption (extracted, CLI-R11) · cmd-waf (extracted, CLI-R11) · cmd-federation (extracted, CLI-R11) · cmd-mail (extracted, CLI-R11) · cmd-dlq (extracted, CLI-R11)
- AI: cmd-ai · cmd-claw · cmd-model
- Templates: cmd-template
- Utilities: cmd-exec · cmd-clean · cmd-reset · cmd-update · cmd-upgrade · cmd-version · cmd-admin · cmd-migrate · cmd-migrate-firebase · cmd-migrate-supabase · cmd-completion
Features
- Features, Overview
- Feature-Auth
- Feature-Storage
- Feature-Search
- Feature-Functions
- Feature-Email
- Feature-Monitoring
- Feature-Plugins
- Feature-nClaw, AI Assistant
- Feature-nChat, Messaging
- Feature-nFamily, Family Social
- Feature-nCloud, Managed Hosting
- Feature-Memory-Rooms, Knowledge Organization
- Feature-Agent-Dashboard, Agent Metrics
- Feature-Image-Generation, AI Image Generation
Configuration
- Configuration, Overview
- Config-Env-Vars
- Config-Postgres
- Config-Hasura
- Config-Auth
- Config-Nginx
- Shared-Proxy
- Config-Optional-Services
- Config-Custom-Services
- Config-System
Plugins (87 + 10 monitoring)
- Plugin-Overview
- Plugin-Catalog
- Plugin-Install
- Plugin-Licensing
- Plugin-Architecture
- Plugin-Dev-Guide
- Plugin-Migrations-Contract
Free (25)
- plugin-backup
- plugin-content-acquisition
- plugin-content-progress
- plugin-cron
- plugin-donorbox
- plugin-feature-flags
- plugin-github
- plugin-github-runner
- plugin-invitations
- plugin-jobs
- plugin-link-preview
- plugin-mdns
- plugin-mlflow
- plugin-monitoring
- plugin-notifications
- plugin-notify
- plugin-paypal
- plugin-search
- plugin-shopify
- plugin-stripe
- plugin-subtitle-manager
- plugin-tokens
- plugin-torrent-manager
- plugin-vpn
- plugin-webhooks
Pro (62)
- plugin-access-controls
- plugin-activity-feed
- plugin-admin-api
- plugin-analytics
- plugin-auth
- plugin-bots
- plugin-browser
- plugin-calendar
- plugin-cdn
- plugin-chat
- plugin-claw
- plugin-claw-budget
- plugin-claw-news
- plugin-claw-web
- plugin-cloudflare
- plugin-cms
- plugin-compliance
- plugin-ddns
- plugin-devices
- plugin-documents
- plugin-entitlements
- plugin-file-processing
- plugin-game-metadata
- plugin-geocoding
- plugin-geolocation
- plugin-google
- plugin-home
- plugin-idme
- plugin-knowledge-base
- plugin-linkedin
- plugin-livekit
- plugin-media-processing
- plugin-meetings
- plugin-moderation
- plugin-mux
- plugin-object-storage
- plugin-observability
- plugin-photos
- plugin-podcast
- plugin-post
- plugin-realtime
- plugin-retro-gaming
- plugin-rom-discovery
- plugin-social
- plugin-sports
- plugin-support
- plugin-tmdb
- plugin-voice
- plugin-web3
- plugin-workflows
Planned (26)
plugin-auditplugin-blogplugin-checkoutplugin-commerceplugin-drmplugin-exportplugin-flowplugin-importplugin-ldapplugin-mailgunplugin-mediaplugin-oauth-providersplugin-pagesplugin-postmarkplugin-rate-limitplugin-reportsplugin-samlplugin-schedulerplugin-sendgridplugin-ssoplugin-subscriptionplugin-thumbplugin-transcoderplugin-twilioplugin-wafplugin-watermark
Guides
- Guide-Production-Deployment
- Guide-SSL-Setup
- Guide-Multi-Tenancy
- Guide-Security-Hardening
- Guide-Monitoring-Setup
- Guide-Backup-Restore
- Guide-Custom-Services
- Guide-Migration-from-v1
Architecture
- Architecture
- Architecture-Guardrails
- Service-Graph
- Compose-Generation
- Nginx-Generation
- Plugin-Architecture
Reference
Machine interface
-
JSON-Output,
--jsonenvelope -
Command-Registry,
nself help --json - Exit-Codes
- error-codes, Error Codes
-
Compat-V15,
NSELF_V15gate
Licensing
Security
Brand
Operations
- operations/release-cascade, Release Cascade
- operations/self-healing, Self-Healing Schema
- operations/redis-tuning, Redis Pool Tuning
- operations/meilisearch-warmup, MeiliSearch Warm-Up
- operations/jwt-rotation, JWT Key Rotation
- operations/windows-wsl2-setup, Windows / WSL2 Setup
- operations/gemini-oauth-reauth, Gemini OAuth Reauth
Contributing
Admin
- USER-ACTION-QUEUE, Pending Admin Actions
All commands (21)
- A: cmd-add · cmd-admin
- B: cmd-backup · cmd-build
- C: cmd-clean · cmd-config
- D: cmd-db · cmd-deploy · cmd-doctor
- E: cmd-exec
- I: cmd-init
- L: cmd-license · cmd-logs
- M: cmd-mcp
- R: cmd-remove · cmd-reset · cmd-restart
- S: cmd-start · cmd-status · cmd-stop
- U: cmd-update