Repository navigation
Lock File
← Image-Lock · Compat-V15
nself.lock.yaml pins what a project runs: the CLI major.minor, the images of the services the CLI owns, the installed plugin versions with their checksums, and the installed bundles. Commit it. nself build writes it, nself build honours its image pins, and nself start --frozen refuses to start a stack that no longer matches it.
The file sits at the project root, not under .nself/, because nself init gitignores .nself/ and git cannot re-include a file under an ignored directory.
# GENERATED BY nself build — DO NOT HAND EDIT (change pins with nself update)
schema_version: 1
cli: "1.5"
images:
hasura: docker.io/hasura/graphql-engine:v2.44.0@sha256:4d34…
postgres: docker.io/library/postgres:16-alpine@sha256:7218…
unpinned:
redis: redis:6
plugins:
ai:
version: 2.0.0
sha256: 9f2c…
tier: licensed
bundles:
- nclaw| Key | Meaning |
|---|---|
cli |
Major.minor of the CLI that wrote the file. A patch upgrade never rewrites it. |
images |
Compose service to image reference with a sha256 digest. |
unpinned |
Service to a tag-only reference whose digest is not known. Listed so the version still cannot move. |
plugins |
Plugin slug to version, sha256 and tier, read from the bundle install-state ledger. |
bundles |
Installed bundles, sorted. |
There are no timestamps. Keys are sorted. Two builds with unchanged inputs leave the file byte-identical, and untouched on disk (the mtime does not move). Only services the CLI owns are listed; your own services and their images are not.
| Mode | Behaviour |
|---|---|
v1.5 (NSELF_V15=1) |
Always on. Every build keeps the lock current. |
| v1.4 (default today) | Off. nself build --write-lock creates it; once the file exists every build honours and updates it. |
nself build --plan lists nself.lock.yaml as a lock artifact when it would change and writes nothing. The apply writes exactly the planned bytes.
When build renders docker-compose.yml, each service image is resolved in this order:
- A variable in the env cascade that the generator reads to build that service's image (
HASURA_VERSION,POSTGRES_IMAGE,REDIS_VERSION; the table below lists them all). - The pin in
nself.lock.yaml. - The CLI image lock (Image-Lock).
So a CLI upgrade never silently moves an existing project's images. To move one, change the env value, or delete its line from the lock and build.
A *_VERSION that is empty, equal to the CLI default (the version nself init writes into .env) or equal to the frozen legacy tag the CLI shipped before the lock means "use the default". It is not an override and never drops a pin, even after a CLI upgrade moves that default. The image lock applies the same test when it renders an image, and it holds for every row in the table below. Only a value that differs from both is an override.
The default test starts with the image-lock entry that produced the rendered image. A few directional compatibility aliases match what the generator does: pgvector accepts the plain-Postgres init version, Valkey accepts the Redis init version, current Hasura accepts the v1.4 init version, and the two auth entries accept both recognized auth defaults. No other entry's default is inherited. For example, pg16 is the pgvector entry's default tag, but POSTGRES_VERSION=pg16 is an explicit override when the project renders plain postgres:pg16; it does not keep an older plain-Postgres pin.
nself init writes *_VERSION defaults into .env. A value that renders to the same image as the pin (HASURA_VERSION=v2.44.0 against a hasura/graphql-engine:v2.44.0@sha256:... pin, with or without the docker.io/ prefix, in legacy or lock pinning mode) keeps the digest. For postgres the comparison uses the image the generator renders (POSTGRES_VERSION=16-alpine matches a pgvector/pgvector:pg16 pin). A different value drops the pin, and build prints one notice naming the variable, once per run. Only a variable the generator really reads for that image counts, whatever its name looks like. One table in the CLI source (internal/compose/image_inputs.go) lists them, and a test fails when it drifts from the generator:
| Service | Variables that feed its image (first wins) |
|---|---|
| postgres |
POSTGRES_IMAGE, then POSTGRES_VERSION (POSTGRES_EXTENSIONS=pgvector picks the pgvector repository) |
| hasura | HASURA_VERSION |
| auth | AUTH_VERSION |
| redis | REDIS_VERSION |
| minio | MINIO_VERSION |
| mailpit | MAILPIT_VERSION |
admin (compose service nself-admin, pinned and compared as admin) |
NSELF_ADMIN_VERSION |
| functions |
FUNCTIONS_VERSION for the node runtime; none for deno and python |
| meilisearch | MEILISEARCH_VERSION |
| typesense | TYPESENSE_VERSION |
| nginx, meilisearch-init | none (fixed image) |
Any other variable (NGINX_VERSION, ADMIN_VERSION, HASURA_IMAGE, STORAGE_IMAGE, MEILISEARCH_INIT_VERSION) has no effect on the image, does not touch the pin and prints no notice. POSTGRES_IMAGE is the only *_IMAGE the generator reads, and it wins over POSTGRES_VERSION. When it names its own digest (POSTGRES_IMAGE=pgvector/pgvector:pg16@sha256:...) it keeps the pin only when that digest is the pin's; another digest at the same tag wins and drops the pin, even when POSTGRES_VERSION is also set. Tags compare as written (V1 and v1 are different tags).
A project that already has a docker-compose.yml gets a first lock that takes its pins from that file, for the services the CLI owns:
- The digest of the local image (
RepoDigests), when the image is present and has one. - Otherwise the CLI image lock entry whose version matches the reference.
- Otherwise a tag-only entry under
unpinned.
A service the old compose lacks takes the CLI default. A project with no compose starts from the CLI defaults.
The admin UI's compose service is nself-admin, while its lock key is admin. If another compose file also defines a service named admin, that service cannot replace the admin UI's lock entry: the generated nself-admin service wins the key deterministically.
The lock is written atomically (temp file, then rename) and ends with the line # end of nself.lock.yaml. A file without that line, such as one cut short by a crash or a full disk, is rejected as damaged instead of loading with fewer pins. If you edit the lock by hand, keep the last line. Every top-level key (schema_version, cli, images, unpinned, plugins, bundles) must be present, as an empty {} or [] when there is nothing to record; a missing or null key is a parse error.
The lock is plain YAML, so a pull request shows exactly which image, plugin or bundle moved. nself build and nself.lock.yaml change together, so review both.
--frozen compares the stack on disk with the lock and fails with E455 (exit 1) when any of these differ:
- the image of a CLI-owned service from its pinned (or tag-only) reference, a pinned service missing from the compose, or a CLI-owned service the lock does not list;
- an image that cannot be read as a plain reference: a CLI-owned service whose
image:contains a variable ($), that hasextendsorbuild, any compose file with a top-levelinclude, or any compose file with more than one YAML document (a trailing---that opens an empty second document counts too), which Compose merges but this check cannot read ("image source cannot be verified"); - an installed plugin version or checksum;
- the installed bundles;
- the CLI major.minor.
The files are merged in the order .nself/compose-files.txt lists them, the order start runs; if you edit that manifest, it must still include docker-compose.yml or --frozen fails with E455. The comparison covers every compose file start runs: the generated file, plugin fragments and docker-compose.override.yml. Docker lets the last file win, so an override that sets a CLI-owned service's image: shows up as an image mismatch, and nself build records it in the lock. Under --frozen a cli or plugin checksum that one side recorded and the other left empty also counts as a mismatch. A plain start still skips empty values.
Not covered: your own services (CS_N) and the services that plugin compose fragments add. Their images are not in the lock and --frozen does not compare them; plugins are covered by their version and sha256. A CLI-owned image built from a variable is not pinned: nself build logs a warning that names it.
The file .nself/state/image-overrides.yml, which start writes when a pull falls back to a registry mirror, is read too, but an entry from it counts only when its digest equals the digest of the image it replaces (same bytes, other registry). Any other override in that file is an image mismatch, and nself build never records it in the lock. When such an entry is the difference, the E455 message names the file. nself build drops the entries whose digest no longer equals the image the compose files render (and removes the file when none is left), so a pin that moved does not leave a stale mirror entry behind; you can also delete the file.
A service pinned by tag only (listed under unpinned) cannot detect a moved tag, so --frozen prints one warning that lists those services.
A project with no lock also fails. Use it in CI and on servers, after nself build has run from the committed lock:
nself build
nself start --frozenUnder --frozen, start does not rebuild and does not auto-install the AI plugins, so the stack it checked is the stack it starts. If the compose file is stale, run nself build first. --frozen cannot be combined with --skip-plugins (usage error E401): the lock describes the full stack, and --skip-plugins starts a smaller one the check never compared.
A plain nself start runs the same comparison, prints one warning that lists the differences, and starts anyway. See cmd-start and error-codes.
Read API for other Go code: lock.Load(projectDir) and (*Lock).PluginVersion(slug). The JSON Schema of the parsed document is schemas/lockfile.v1.schema.json.
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