From fbf29fa1d8f207c8c59da2f29e823fa27b399283 Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Fri, 6 Nov 2026 15:36:36 -0500 Subject: [PATCH 1/3] Document nspawn 3.0.0 - hugo.yaml and reference.md: documented version 3.0.0. - docker-api.md: the host's socket (nspawn-api.socket, polkit for each client, the drop-in for /run/docker.sock), the docker command and GitLab Runner through it, what the API serves and what answers 501, API names; a machine's socket takes every request read-write and reads, follows logs and kills with :ro; the limits of a socket. The examples were run on the Arch test VM with the 3.0.0 package. - reference.md: -P/--publish-all on create and start, volume ls --filter, volume create --label, volume inspect, system prune --volumes, the auto timezone mode, daemon --install and api-serve of the host's socket. - overview.md: the Docker API as a client of the service, the bus's error names, the host's socket asking polkit, volume inspect. - machines.md: variables with line breaks, the auto timezone mode, volume labels, filters and inspect, cp and a stopped machine's volumes. - networking.md: -P/--publish-all. - images.md: system prune --volumes. - faq.md, getting-started.md: what the Docker API serves. --- content/en/docs/docker-api.md | 232 ++++++++++++++++++++++------- content/en/docs/faq.md | 8 +- content/en/docs/getting-started.md | 4 +- content/en/docs/images.md | 4 +- content/en/docs/machines.md | 38 +++-- content/en/docs/networking.md | 4 +- content/en/docs/overview.md | 29 ++-- content/en/docs/reference.md | 44 ++++-- hugo.yaml | 2 +- 9 files changed, 265 insertions(+), 100 deletions(-) diff --git a/content/en/docs/docker-api.md b/content/en/docs/docker-api.md index 9782e35..96a75e3 100644 --- a/content/en/docs/docker-api.md +++ b/content/en/docs/docker-api.md @@ -2,31 +2,171 @@ title: The Docker API weight: 8 description: >- - docker's socket for reverse proxies and dashboards, and auto-update of the - machines whose tag points to a newer image. + The Docker Engine API on a socket of the host and in each machine that mounts + docker's socket, for the docker command, CI runners, reverse proxies and + dashboards, and auto-update of the machines whose tag points to a newer image. --- +nspawn serves the Docker Engine API, 1.51 and any version down to 1.24 a client +asks for, on a socket of the host and in each machine that mounts docker's +socket. Clients make, start, attach to, exec in, copy files to and from and +remove containers, pull and remove images, and make, join and remove volumes and +networks. The server is a client of the [service](/docs/overview/#the-service), +as the command line is: every change it makes is a method of `org.nspawn`, under +the same polkit actions, and what it makes is a machine like any other, which +`nspawn ps` shows. + +## The host's socket + +The packages ship it turned off: + +```shell +sudo systemctl enable --now nspawn-api.socket +export DOCKER_HOST=unix:///run/nspawn/api.sock +``` + +Every user may connect to `/run/nspawn/api.sock`, and polkit decides what each +one may do, with the service's actions: reads and waits ask for +`org.nspawn.inspect`, logs and changes for `org.nspawn.manage`, `_ping` and +`version` for nothing. polkit is never asked to prompt, since nothing on a socket +answers an authentication agent: a user other than root needs a rule, such as +`nspawn-wheel.rules`, which the packages ship as an example. Root may do +everything. + +Then the `docker` command works with it: + +```text +$ docker pull busybox:1.37 +1.37: Pulling from library/busybox +docker.io/library/busybox:1.37: manifest 66a6306db78b with 1 layer(s), assembling as overlay +68fe9bff2ad4: Already exists +30ecbe150909: Already exists +Digest: sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec +Status: Downloaded newer image for busybox:1.37 +docker.io/library/busybox:1.37 +$ docker run --rm busybox:1.37 echo hello +hello +$ docker run -d --name web -p 8080:80 busybox:1.37 httpd -f -p 80 -h /tmp +c7dac8ca487657695c14e21240f15fd99e7eac48d6c13e770febe7b0ac74d31e +$ echo hello > index.html && docker cp index.html web:/tmp/index.html +$ curl -s http://127.0.0.1:8080/ +hello +$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' +NAMES IMAGE STATUS PORTS +web busybox:1.37 Up Less than a second 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp +$ docker exec web sh -c 'id; pwd' +uid=0(root) gid=0(root) +/ +$ sudo nspawn ps + MACHINE IMAGE MODE COMMAND STATE UP PID NETWORK OS + web docker.io/library/busybox:1.37 app httpd -f -p 80 -h /tmp running 0s 2814430 10.99.0.3 8080->80/tcp - +``` + +Volumes and networks are nspawn's: + +```text +$ docker volume create data +data +$ docker network create back +68735561ab7cd7e4da9b248b9c68ff65d4518c05d1730d79056e8dd3c2dc8eda +$ docker network connect back web +$ docker run --rm --network back -v data:/data busybox:1.37 sh -c 'wget -qO- http://web/ | tee /data/page' +hello +$ docker run --rm -v data:/data busybox:1.37 cat /data/page +hello +``` + +On a host without docker, clients that only look at `/run/docker.sock` or +`/var/run/docker.sock` find the socket there with a drop-in: + +```shell +sudo mkdir -p /etc/systemd/system/nspawn-api.socket.d +printf '[Socket]\nSymlinks=/run/docker.sock\n' | sudo tee /etc/systemd/system/nspawn-api.socket.d/docker.conf +sudo systemctl daemon-reload && sudo systemctl restart nspawn-api.socket +``` + +### GitLab Runner + +GitLab Runner's docker executor runs its jobs through the socket: the clone, the +job's image, services with their aliases, artifacts, cache volumes and a network +per build (`FF_NETWORK_PER_BUILD`). In its `config.toml`: + +```toml +[[runners]] + executor = "docker" + [runners.docker] + host = "unix:///run/nspawn/api.sock" + image = "alpine:3.22" +``` + +The runner runs as root, or as a user that a polkit rule lets through. + +## What it serves + +- `_ping`, `version`, `info` and `system df`. +- Containers: list, inspect, logs, stats, top, events; create, start, stop, + restart, kill, pause, unpause, wait, update, remove and prune. A create's + settings become the machine's options. What nspawn has no use for is named in + the answer's `Warnings` (`CpuShares`, `SecurityOpt`, a logging driver other than + `json-file`, `local`, `journald` or `none`, among others); what would change what + the container does is refused with 400 (`PidMode`, `IpcMode` or `UTSMode` of the + host, a user namespace mode, another runtime, `VolumesFrom`, an IPv6 address). +- Attach, with the input: `docker run -i`, `docker run -it` and `docker start -ai` + give the program their input, and an attach that keeps the input gets the output + byte for byte. +- Exec: a terminal or the two streams apart, the input, variables, working + directory and user. On a terminal, the end of the input or the client's hangup + sends the command SIGHUP. +- Copies: `docker cp` both ways, on running containers and on stopped ones, owners + and modes kept. A volume of a stopped container is mounted only while it runs, so + a copy to or from it is refused until it starts. +- Images: list, inspect, history, pull (`POST /images/create`), remove and prune. + A record pulled or built that never ran is an image, not a container; a machine + made from it is a container. An image's ID is its config's digest. +- Volumes and networks: list, inspect, create, remove and prune; connect and + disconnect with aliases and fixed addresses. `bridge`, `host` and `none` are the + pre-defined networks. + +Builds, loads, imports, tags, pushes and commits of images, container renames and +exports, swarm and plugins answer 501 with "not supported". + +A name the API takes that is no machine name, such as `reaper_1`, stays the +container's name in the API, and its machine gets one made of it (`reaper-1-` and +eight hex digits), which `nspawn ps` shows. A network keeps the name the API gave +it the same way. + ## docker's socket in a machine -Tools that follow containers through docker's socket find nspawn's machines the -same way: reverse proxies that route by labels (caddy-docker-proxy, Traefik, +A machine that mounts `/var/run/docker.sock`, `/run/docker.sock` or +`/run/nspawn/api.sock` from the host gets nspawn's Docker API there instead of the +host's docker, with `-v /var/run/docker.sock:/var/run/docker.sock` or compose's +`use_api_socket`. Mounted read-write, it takes every request the API serves: + +```text +$ sudo nspawn run --rm -v /var/run/docker.sock:/var/run/docker.sock \ + docker.io/library/docker:28.5.1-cli -- docker run --rm busybox:1.37 echo from-a-machine +docker-28.5.1-cli-6fd52594: 10 layer(s) shared with docker-28.5.1-cli, assembling as overlay +from-a-machine +``` + +Mounted with `:ro`, it reads, follows logs and kills, and refuses any other change +with 403: + +```text +$ sudo nspawn run --rm -v /var/run/docker.sock:/var/run/docker.sock:ro \ + docker.io/library/docker:28.5.1-cli -- docker run --rm busybox:1.37 true +docker-28.5.1-cli-c46d6cef: 10 layer(s) shared with docker-28.5.1-cli, assembling as overlay +docker: Error response from daemon: this machine binds nspawn's Docker API read-only + +Run 'docker run --help' for more information +``` + +Tools that follow containers through the socket find nspawn's machines the same +way: reverse proxies that route by labels (caddy-docker-proxy, Traefik, nginx-proxy with docker-gen and acme-companion), dashboards (homepage) and log -viewers (Dozzle). A machine that mounts `/var/run/docker.sock` or -`/run/docker.sock` from the host, with `-v /var/run/docker.sock:/var/run/docker.sock` -(`:ro` is taken too) or compose's `use_api_socket`, gets nspawn's Docker API -there instead of the host's docker. - -It speaks the Docker API 1.51, and any version down to 1.24 a client asks for: -`_ping`, `version` and `info`, the machines as containers with their labels, -networks and addresses, inspect with the environment, events as they happen, -logs, stats and top, and the networks. Reads of images, volumes and swarm -objects get 501. It changes nothing, with one exception: -`POST /containers/ID/kill` sends a signal as `nspawn kill` does, so that -docker-gen and acme-companion can have nginx read new configuration and -certificates. Any other write is refused with 403. - -caddy-docker-proxy in front of a service, from a `compose.yaml` in a directory -`proxy`: +viewers (Dozzle). They only read, and kill to have a server read its +configuration again, so `:ro` is all they need. caddy-docker-proxy in front of a +service, from a `compose.yaml` in a directory `proxy`: ```yaml services: @@ -37,7 +177,7 @@ services: environment: - CADDY_INGRESS_NETWORKS=proxy_default volumes: - - /var/run/docker.sock:/var/run/docker.sock + - /var/run/docker.sock:/var/run/docker.sock:ro whoami: image: traefik/whoami labels: @@ -52,42 +192,28 @@ Hostname: proxy-whoami-1 IP: 127.0.0.1 ``` -What a client sees, from a machine that mounts the socket: - -```text -$ sudo nspawn run --rm -u 0 -v /var/run/docker.sock:/var/run/docker.sock \ - docker.io/curlimages/curl:8.11.1 -- -s --unix-socket /var/run/docker.sock http://localhost/_ping -OK -$ sudo nspawn run --rm -u 0 -v /var/run/docker.sock:/var/run/docker.sock \ - docker.io/curlimages/curl:8.11.1 -- -s --unix-socket /var/run/docker.sock -X POST http://localhost/containers/create -{"message":"nspawn's Docker API is read-only: POST /containers/create is not served"} -``` - -A machine that has it sees every machine and their -`-e` variables (`--secret` values never show). Only root inside the machine -can connect; the curl image runs as a user of its own, hence `-u 0`, and without -it the connection is refused: - -```text -$ sudo nspawn run --rm -v /var/run/docker.sock:/var/run/docker.sock \ - docker.io/curlimages/curl:8.11.1 -- -sS --unix-socket /var/run/docker.sock http://localhost/_ping -curl: (7) Failed to connect to localhost port 80 after 0 ms: Could not connect to server -``` +A machine that has the socket sees every machine and their `-e` variables +(`--secret` values never show). Only root inside the machine can connect. ### How it is served Each machine that takes the socket has its own, `/run/nspawn/api/NAME.sock`, -opened by systemd (`nspawn-api@NAME.socket`) before the machine starts and -closed after it stops. The server behind it, `nspawn-api@NAME.service`, -starts when something connects and leaves after a minute without a -connection. A container ID comes from the machine's name and creation time, so -it stays across restarts and updates. nspawn binds the machine's -`/etc/hostname` from a directory named after that ID, so tools -that look for their own container in `/proc/self/mountinfo` find it. - -Each machine's API takes 64 connections at once and closes any past that. At -most 16 of them can stream logs or events, each running a `journalctl`; one more -gets 503. mstack machines cannot take the socket. +opened by systemd (`nspawn-api@NAME.socket`) before the machine starts and closed +after it stops; the host's is `nspawn-api.socket`. The server behind each, +`nspawn-api@NAME.service` or `nspawn-api.service`, starts when something connects +and leaves a minute after the last connection, the attached sessions and exec +commands of its clients included. A container ID comes from the machine's name and +creation time, so it stays across restarts and updates. nspawn binds the machine's +`/etc/hostname` from a directory named after that ID, so tools that look for their +own container in `/proc/self/mountinfo` find it. + +What one socket's clients hold at once is bounded: 64 connections (one more is +closed at once), 16 of them logs, events or attach streams, each running a +`journalctl`, 16 waits and attaches, and 64 exec commands that run; one more of +any of these gets 503. On the host's socket a user other than root holds a quarter +of the connections and of the exec commands and half of the streams and of the +waits at most, so that no user keeps the others or root out, and root has 16 +streams of its own. mstack machines cannot take the socket. ## auto-update diff --git a/content/en/docs/faq.md b/content/en/docs/faq.md index f58aab1..d7c00a6 100644 --- a/content/en/docs/faq.md +++ b/content/en/docs/faq.md @@ -27,10 +27,10 @@ No. nspawn's machines are systemd-nspawn containers managed by systemd: they are `systemd-nspawn@NAME.service` units, appear in `machinectl list`, log to journald and boot a full init when the image has one. nspawn's own service holds none of them open. docker compose -files run with `nspawn compose`, Dockerfiles build with `nspawn build`, and a -machine that mounts `/var/run/docker.sock` gets a Docker API that reverse -proxies and -dashboards read, but there is no orchestration across hosts. +files run with `nspawn compose`, Dockerfiles build with `nspawn build`, and the +Docker Engine API, on a socket of the host and in machines that mount +`/var/run/docker.sock`, serves the `docker` command, CI runners, reverse proxies +and dashboards, but there is no orchestration across hosts. ## Can I run images from Docker Hub? diff --git a/content/en/docs/getting-started.md b/content/en/docs/getting-started.md index 3651a9a..e2a6354 100644 --- a/content/en/docs/getting-started.md +++ b/content/en/docs/getting-started.md @@ -221,7 +221,7 @@ keeps your credentials for that registry only; see - [Compose](/docs/compose/): docker compose files, run by `nspawn compose`. - [Building images](/docs/building/): Dockerfiles and mkosi with `build`, and `push`. -- [The Docker API](/docs/docker-api/): the API nspawn serves to reverse proxies - and dashboards, and `auto-update`. +- [The Docker API](/docs/docker-api/): the API nspawn serves to the `docker` + command, CI runners, reverse proxies and dashboards, and `auto-update`. - [Configuration](/docs/configuration/): `/etc/nspawn/nspawn.toml`, environment variables and flags. diff --git a/content/en/docs/images.md b/content/en/docs/images.md index d6fa81a..0c061a4 100644 --- a/content/en/docs/images.md +++ b/content/en/docs/images.md @@ -339,7 +339,9 @@ made from; without `-a` it removes the dangling ones, which nspawn's store never has, since a tag pulled or built again replaces its record. `system prune` removes stopped machines made from an image (by `create` or `run`), networks no machine uses and the build cache no image uses; with `-a` also the images no -machine was made from and the whole build cache. `--filter label=...` and +machine was made from and the whole build cache, and with `--volumes` the volumes +no machine uses that were made without a name, which only the Docker API makes. +`--filter label=...` and `until=...` narrow what goes, and `-f` skips the question. `builder prune` takes the [build cache](/docs/building/#the-build-cache) alone. diff --git a/content/en/docs/machines.md b/content/en/docs/machines.md index f694d98..b8f2063 100644 --- a/content/en/docs/machines.md +++ b/content/en/docs/machines.md @@ -224,8 +224,11 @@ say otherwise: - An argument may hold newlines, as a script for `sh -c` does, from the command line, compose or the image's own config: the program is then started through the image's `/bin/sh`, which gets the arguments encoded and runs the program - with them as they were given. An image without `/bin/sh` cannot take such an - argument. + with them as they were given. A variable's value may hold line breaks the + same way (`-e`, compose's `environment`): it goes in encoded and the image's + `/bin/sh` turns it back before the program runs; its name is then one of + letters, digits and underscores. An image without `/bin/sh` cannot take such + an argument or variable. - `--entrypoint PROGRAM` replaces the entrypoint with one program and drops the image's `cmd`, so only the arguments after `--` follow it (`--entrypoint /bin/sh` alone is a shell). `--entrypoint ""` drops the entrypoint, so the @@ -282,7 +285,7 @@ sudo nspawn start web -v /srv/www:/usr/share/nginx/html:ro -v pgdata:/var/lib/po - `-v` is repeatable and remembered; `-v none` forgets every volume. - `--mount type=bind|volume|tmpfs,source=...,target=...` is the long form of `-v` and `--tmpfs` (`readonly`, `tmpfs-size`, `tmpfs-mode`); a volume needs - its name, since nspawn has no anonymous volumes, and a source or target with + its name, as a mount makes no anonymous volume, and a source or target with `:` is refused. In machines that run with private users (every machine but an app on a bridge @@ -296,10 +299,11 @@ otherwise. On overlay and flat the volumes come from the settings file and are there before the init even runs. Named volumes outlive the machines that use them. `nspawn volume ls` lists them -with the machines whose records mount them, `volume create NAME` makes one -ahead of its first use (filled from the image of the first machine that takes it -while it is empty), and `volume rm` and `volume prune` remove the ones no -machine uses; a volume a machine still names is refused until that machine is +with the machines whose records mount them (`--filter label=KEY[=VALUE]` only +those with the label), `volume create NAME` makes one ahead of its first use +(filled from the image of the first machine that takes it while it is empty; +`--label KEY=VALUE` labels it), `volume inspect` shows what nspawn keeps of them +as JSON, and `volume rm` and `volume prune` remove the ones no machine uses; a volume a machine still names is refused until that machine is started with other volumes (or `-v none`) or removed. ## Labels @@ -526,10 +530,13 @@ its unit: `stop` still wins, and `-1` waits for as long as the program takes. `kill` reads the stop signal to decide whether the restart policy applies. - `--timezone MODE`: how systemd-nspawn sets the machine's `/etc/localtime` at - each start. Its default, `auto`, points it at the host's zone every time, so - a zone set inside with `timedatectl` does not survive a restart; `off` leaves - the machine's own, and `copy`, `bind`, `symlink` and `delete` are its other - modes. An app also takes `-e TZ=`. + each start. Its default, `auto`, points it at the host's zone every time: a + link where the machine's zone database has that zone, otherwise a copy of the + host's file, which a package installed inside, such as tzdata, may replace (a + read-only root gets the host's file mounted over it). A zone set inside with + `timedatectl` does not survive a restart; `off` leaves the machine's own, and + `copy`, `bind`, `symlink` and `delete` are its other modes. An app also takes + `-e TZ=`. - `--log-driver DRIVER`: where the program's output goes. `local`, the default, sends it to journald's `nspawn` namespace, a journal of nspawn's own apart from the system's, so that a program that writes a lot never @@ -564,8 +571,9 @@ back to the default with an empty string (`--hostname ""`), `--shm-size` with machine, the target of a volume or a secret, a `--tmpfs` or a `--device` path, must be plain: a `.` or `..` component is refused, since it would land elsewhere once mounted. A path the image -declares as a volume with nothing mounted over it gets a note at start: nspawn -has no anonymous volumes, so what is written there goes with the machine. +declares as a volume with nothing mounted over it gets a note at start: no +anonymous volume is made for it, so what is written there goes with the +machine. ## GPUs and CDI devices @@ -755,7 +763,9 @@ not: what was copied before it stays copied. A stopped overlay or flat machine can be copied into and out of; a stopped -`mstack` machine cannot, since its tree only exists while it runs. Where +`mstack` machine cannot, since its tree only exists while it runs. A stopped +machine's volumes are mounted only while it runs, so a path at or below one of +them, or a source that holds one, is refused until it starts. Where SELinux enforces, a host directory mounted with `-v` keeps its own label, which the service may not be allowed to write to; named volumes are nspawn's own and always work. diff --git a/content/en/docs/networking.md b/content/en/docs/networking.md index cd9af8d..f3c79ef 100644 --- a/content/en/docs/networking.md +++ b/content/en/docs/networking.md @@ -241,7 +241,9 @@ holds. A host range with one port of the machine, `-p 8000-8010:80`, gets the first free port of the range, and `-p 80-82` a free host port for each port. `create` picks them, or the `start` that gave them. The long form, `published=8080,target=80,protocol=udp,host_ip=127.0.0.1`, has the same fields, -`target` the only one required. +`target` the only one required. `-P`/`--publish-all` publishes each TCP and UDP +port the image exposes (`EXPOSE`) that no `-p` publishes to a free port of the +host. The list is remembered for the machine, the host ports it got included: `nspawn start web` next time publishes the same ports, and `-p none` forgets diff --git a/content/en/docs/overview.md b/content/en/docs/overview.md index 6d990e0..dad56b8 100644 --- a/content/en/docs/overview.md +++ b/content/en/docs/overview.md @@ -52,11 +52,14 @@ machines: directory with a `mkosi.conf`, and imports the result as a local image; `push` uploads an image to a registry, skipping the layers that are already there. See [Building](/docs/building/). -- A machine that mounts `/var/run/docker.sock` gets a Docker API there that - changes nothing but sends `kill`, so +- The Docker Engine API, on a socket of the host (`nspawn-api.socket`) and in + each machine that mounts `/var/run/docker.sock`: the `docker` command, GitLab + Runner's docker executor and other clients make, run, attach to, exec in and + copy to and from containers, pull images and make volumes and networks, and reverse proxies that route by labels and dashboards find the machines as - containers, and `auto-update` moves labelled machines to the image their tag - names now. See [The Docker API](/docs/docker-api/). + containers. The server is a client of the service, as the command line is. + `auto-update` moves labelled machines to the image their tag names now. See + [The Docker API](/docs/docker-api/). - `generate` prints the commands that make the same machines on another host, every flag they keep included. @@ -65,8 +68,9 @@ line is one of its clients, the way `machinectl` and `systemctl` are clients of machined and systemd. The service is started by the bus when a command arrives and exits when it has been idle for a minute. Between commands only these run: the healthcheck of a machine that has one (`nspawn-health-NAME.service`), the -Docker API of a machine that asked for one, which systemd starts when a client -connects, and the daily `auto-update` timer once you enable it. See +Docker API of a machine that asked for one and the host's once you enable it, +which systemd starts when a client connects, and the daily `auto-update` timer +once you enable it. See [The service](#the-service). It is one binary written in Rust. Its own state lives under `/var/lib/nspawn`, @@ -82,8 +86,11 @@ create, rm, images rm and prune, cp, volume rm and prune, secret rm, network rm, connect, disconnect and prune, builder prune, auto-update) come back as job objects that report their output and result, and `exec`, `logs`, `events` and an attached `run` hand their streams or terminal over the bus, with a process object -that says how they ended. Scripts that would rather not speak D-Bus get the same -dictionaries as JSON from `inspect` and `--json` on the listings. +that says how they ended. A method or job that fails says what failed with a +name under `org.nspawn.Error` (`NoSuchMachine`, `InUse`, `Invalid` and the rest) +and the message the command line prints. Scripts that would rather not speak +D-Bus get the same dictionaries as JSON from `inspect` and `--json` on the +listings. Who may call what is polkit's answer, the way it is for machined and systemd. The bus lets everyone in and the service asks polkit about the caller, under @@ -91,14 +98,16 @@ two actions: | Action | Methods | | --- | --- | -| `org.nspawn.inspect` | The ones that only read: `images ls`, `ps` and `machines ls`, `inspect`, `generate`, `top`, `stats`, `events`, `wait`, `port`, `system df`, `builder du`, `network ls` and `network inspect`, `volume ls`, `secret ls` and `secret inspect`. | +| `org.nspawn.inspect` | The ones that only read: `images ls`, `ps` and `machines ls`, `inspect`, `generate`, `top`, `stats`, `events`, `wait`, `port`, `system df`, `builder du`, `network ls` and `network inspect`, `volume ls` and `volume inspect`, `secret ls` and `secret inspect`. | | `org.nspawn.manage` | Everything else: pulling, building, starting, stopping, restarting, pausing, removing, `exec`, `shell`, `cp`, `logs`, volumes, secrets, the registry commands and the credentials. | Both are for administrators by default, so `sudo nspawn ...` works, and a desktop session (or a terminal where you started `pkttyagent`) is asked for a password instead. Root is never asked, which is also how the service keeps working where polkit is not installed: there, nobody but root can -call it. One interface asks nothing: `org.nspawn.Names` hands out the names of +call it. The host's socket of the [Docker API](/docs/docker-api/) asks polkit the +same two actions about each of its clients, never letting it prompt. One +interface asks nothing: `org.nspawn.Names` hands out the names of machines, images, networks and volumes, and nothing else about them, to any user. diff --git a/content/en/docs/reference.md b/content/en/docs/reference.md index ac97c11..a2d0ae8 100644 --- a/content/en/docs/reference.md +++ b/content/en/docs/reference.md @@ -3,7 +3,7 @@ title: Command reference linkTitle: Reference weight: 10 description: >- - Every command and option of nspawn 2.4.0. + Every command and option of nspawn 3.0.0. --- `nspawn --help` and `nspawn COMMAND --help` list the options; this page @@ -125,7 +125,7 @@ size, speed and time left; in a pipe or a log only the lines are written. ```text nspawn create SOURCE NAME [--backend BACKEND] [--network NETWORK]... [--network-alias [NETWORK=]NAME]... - [-p [IP:][HOST:]CONTAINER[/udp]]... [--entrypoint PROGRAM] [-e VAR[=VALUE]]... + [-p [IP:][HOST:]CONTAINER[/udp]]... [-P] [--entrypoint PROGRAM] [-e VAR[=VALUE]]... [-v SOURCE:TARGET[:ro]]... [-l KEY=VALUE]... [--restart POLICY] [-m SIZE] [--memory-swap SIZE] [--cpus N] [--pids-limit N] [HEALTHCHECK OPTIONS] [OTHER OPTIONS] [--interface IFACE]... [--secret SECRET]... [--depends-on NAME[:CONDITION][:optional]]... [--env-file FILE]... [--label-file FILE]... @@ -144,6 +144,7 @@ the image's own name, as `run` does. The layers are shared with the source. | `--network NETWORK` | Networks of the new machine, as for [start](#start). Default: the source's kind; a network made with `network create` is not inherited, as ports and labels are not. | | `--network-alias [NETWORK=]NAME` | Other names of the machine on its networks, as for [start](#start). | | `-p`, `--publish [IP:][HOST:]CONTAINER[/udp]` | Ports to publish on the host, like `start -p`. Not inherited from the source. | +| `-P`, `--publish-all` | Publish each port the image exposes, like `start -P`. | | `--entrypoint PROGRAM` | Replace the image's entrypoint and drop its cmd, so only the arguments given follow it; an empty string runs the arguments alone. An app left with nothing to run is refused with `no command specified`. App images only. | | `-e`, `--env VAR[=VALUE]` | Environment for the program, `VAR=value` or `VAR` copied from the calling shell. App images only. | | `-v`, `--volume SOURCE:TARGET[:ro]` | Mount a host directory or a named volume; `z`, `Z`, a propagation, `nocopy` and `cached` are taken and do nothing. | @@ -362,7 +363,7 @@ comes along. See ## start ```text -nspawn start NAME [--network NETWORK]... [--network-alias [NETWORK=]NAME]... [-p [IP:][HOST:]CONTAINER[/udp]]... +nspawn start NAME [--network NETWORK]... [--network-alias [NETWORK=]NAME]... [-p [IP:][HOST:]CONTAINER[/udp]]... [-P] [--entrypoint PROGRAM] [-e VAR[=VALUE]]... [-v SOURCE:TARGET[:ro]]... [-l KEY=VALUE]... [--restart POLICY] [-m SIZE] [--memory-swap SIZE] [--cpus N] [--pids-limit N] [--health-cmd COMMAND] [--health-interval DURATION] [--health-timeout DURATION] [--health-retries N] @@ -385,6 +386,7 @@ Boots an image as a machine. Every option is remembered for the next start. | `--network NETWORK` | Network of the machine: `bridge`, the default network (the default); the name of a network made with [network create](#network-create); `veth`, a veth pair configured by systemd-networkd on the host (booted images only); `host`, the host's own network; `none`, no interface but `lo`; or `container:NAME`, the network namespace of that running machine (app images only; see [container:NAME](/docs/networking/#containername)). Repeatable for several bridge networks, the first one primary: its address is where published ports lead and its gateway the default route, unless it is internal, in which case the first network that is not has the route. `veth`, `host`, `none` and `container:NAME` go alone. | | `--network-alias [NETWORK=]NAME` | Another name for the machine on its primary network, or on `NETWORK`: every member of that network resolves it. Repeatable; `none` forgets them. | | `-p`, `--publish [IP:][HOST:]CONTAINER[/udp]` | Publish a port on the host: on every IPv4 and IPv6 address of the host, or on `IP` alone (`127.0.0.1:8080:80`, `[::1]:8080:80`); `0.0.0.0` is every IPv4 address, `[::]` every IPv6 one. No host port (`80`, `:80`) or 0 takes a free one of the host, kept by the machine; a host range with one port of the machine (`8000-8010:80`) the first free one in it. `8000-8010:8000-8010` publishes a range, one mapping per port. Also `published=HOST,target=CONTAINER[,protocol=udp][,host_ip=IP]`, `target` the only one required. Repeatable; `none` forgets them all. | +| `-P`, `--publish-all` | Publish each TCP and UDP port the image exposes (`EXPOSE`) that no `-p` publishes to a free port of the host. `--dns-option` and `--expose` are taken with a warning that they are ignored. | | `--ip [NETWORK=]IP` | A fixed IPv4 address on the primary network, or on `NETWORK`: one made with [network create](#network-create), inside its subnet and nobody else's there. Repeatable, once per network. | | `--entrypoint PROGRAM` | Replace the image's entrypoint and drop its cmd, so only the arguments given follow it; an empty string runs the arguments alone. An app left with nothing to run is refused with `no command specified`. App images only. | | `-e`, `--env VAR[=VALUE]` | Environment for the program, `VAR=value` or `VAR` copied from the calling shell. Repeatable; `none` forgets them. App images only. | @@ -423,7 +425,7 @@ Boots an image as a machine. Every option is remembered for the next start. | `--oom-score-adj N` | OOM score adjustment of the machine, -1000 to 1000. | | `--stop-signal SIGNAL` | Signal `stop` sends the program, instead of the image's (`SIGTERM`). | | `--stop-timeout SECONDS` | Seconds `stop` waits after the signal before SIGKILL, unless `-t` says otherwise; `-1` waits for as long as the program takes. Default: 10. | -| `--timezone MODE` | How systemd-nspawn sets the machine's `/etc/localtime` at each start, its `Timezone=`: `auto` (the default, the host's zone), `off` (left alone, so a zone set inside stays), `copy`, `bind`, `symlink` or `delete`. `auto` forgets the setting. | +| `--timezone MODE` | How systemd-nspawn sets the machine's `/etc/localtime` at each start, its `Timezone=`: `auto` (the default, the host's zone: a link where the machine's zone database has it, otherwise a copy of the host's file, which a package installed inside may replace; a read-only root gets the host's file mounted over it), `off` (left alone, so a zone set inside stays), `copy`, `bind`, `symlink` or `delete`. `auto` forgets the setting. | | `--log-driver DRIVER` | Where the program's output goes: `local` (the default: journald's `nspawn` namespace, a journal of nspawn's own apart from the system's, capped at 1 GiB, which `logs` and an attached `run` read), `journal` (the system's journal) or `none` (dropped; `logs` refuses the machine, an attached `run` of an app still shows the output straight from the program, and a booted machine runs with `-d`). | | `--init`, `--init=false` | The program under systemd-nspawn's stub init, which reaps orphans and turns a poweroff into SIGTERM, or as PID 1 of the machine. Without either, PID 1 for an image that carries s6-overlay, the stub init otherwise. App images only. | | `--sysctl KEY=VALUE` | A `net.*` sysctl for an app machine's network namespace, after the two it gets by default, `net.ipv4.ping_group_range=0 2147483647` and `net.ipv4.ip_unprivileged_port_start=0`, so that a user other than root can ping without raw sockets and bind a port below 1024. Repeatable; `none` forgets them. | @@ -849,22 +851,34 @@ Named volumes, the directories `-v NAME:/path` makes under ### volume ls ```text -nspawn volume ls [--json] +nspawn volume ls [--json] [--filter label=KEY[=VALUE]]... ``` Lists the named volumes with the machines whose records mount them, when each -was made and its path. `--json` prints the list as the service returns it. -`volume list` is an alias. +was made and its path. `--filter label=KEY` or `label=KEY=VALUE` lists only +those with the label, every filter holding. `--json` prints the list as the +service returns it. `volume list` is an alias. ### volume create ```text -nspawn volume create NAME +nspawn volume create NAME [--label KEY=VALUE]... ``` Makes a volume ahead of its first use (`start` makes it otherwise): a -directory owned by root, mode 0755. One that exists already is fine. Names have -letters, digits, `_`, `.` and `-`, not starting with a dot. +directory owned by root, mode 0755. One that exists already is fine, and keeps +its own labels. Names have letters, digits, `_`, `.` and `-`, not starting with +a dot. `--label` labels it; labels and the creation time are kept beside the +volume's directory, never inside it. + +### volume inspect + +```text +nspawn volume inspect NAME... +``` + +What nspawn keeps about the volumes, as JSON: their path, the machines that +mount them, when each was made and their labels. ### volume rm @@ -960,8 +974,9 @@ no machine uses and the build cache no image shares, after asking unless `-f` is given; `-a` also removes every image no machine was made from and the whole build cache. A `label=` filter leaves the build cache alone. -`--volumes` asks for anonymous volumes too, which nspawn has none of: it is -accepted with a note. `--filter` takes `label=KEY`, `label=KEY=VALUE` and +`--volumes` also removes the volumes no machine uses that were made without a +name, which only the Docker API makes, and counts what they held in the space +reclaimed; it takes no `until=` filter. `--filter` takes `label=KEY`, `label=KEY=VALUE` and `until=DURATION` or unix seconds. ## builder @@ -1010,7 +1025,7 @@ not a command to type; `--install` is. | Option | Meaning | | --- | --- | -| `--install` | Write the bus policy, the polkit actions, the activation file and the unit that let the bus start this binary on demand, the units of the Docker API socket and of auto-update (its timer disabled), and the journald configuration of nspawn's log namespace, then return. A package installs the same files. | +| `--install` | Write the bus policy, the polkit actions, the activation file and the unit that let the bus start this binary on demand, the units of the Docker API sockets, a machine's and the host's (the host's not enabled), and of auto-update (its timer disabled), and the journald configuration of nspawn's log namespace, then return. A package installs the same files. | | `--idle-exit SECONDS` | Exit after this long without a call or a running job. Default: 60; `0` keeps serving. | ## completions @@ -1056,5 +1071,6 @@ the `--help` texts. with `--depends-on`, and `nspawn health-run NAME` is what `nspawn-health-NAME.service` runs to probe a machine's healthcheck. `nspawn attach-exec NAME -- ...` is the drop-in's `ExecStart=`, `nspawn api-serve NAME` -is what `nspawn-api@NAME.service` runs, and `nspawn remove-after-exit` removes a +is what `nspawn-api@NAME.service` runs and `nspawn api-serve` what +`nspawn-api.service` runs, and `nspawn remove-after-exit` removes a `run --rm` machine. They are not meant to be typed and are hidden from `--help`. diff --git a/hugo.yaml b/hugo.yaml index 3475721..c2c69e4 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -53,7 +53,7 @@ params: privacy_policy: /about/#privacy # Version of nspawn the documentation describes. - version: 2.4.0 + version: 3.0.0 archived_version: false # In-page links to open issues and suggest changes. From 54ad5099fdcd1f6cd27244400f7cde8f40f94c4f Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Fri, 6 Nov 2026 17:02:04 -0500 Subject: [PATCH 2/3] Document the short name of an image built here in the Docker API --- content/en/docs/docker-api.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/content/en/docs/docker-api.md b/content/en/docs/docker-api.md index 96a75e3..f9abeaa 100644 --- a/content/en/docs/docker-api.md +++ b/content/en/docs/docker-api.md @@ -122,7 +122,10 @@ The runner runs as root, or as a user that a polkit rule lets through. a copy to or from it is refused until it starts. - Images: list, inspect, history, pull (`POST /images/create`), remove and prune. A record pulled or built that never ran is an image, not a container; a machine - made from it is a container. An image's ID is its config's digest. + made from it is a container. An image's ID is its config's digest. An image built + here with a name without a registry (`nspawn build -t somos/app:1`, which nspawn + files as `hub.nspawn.org/somos/app:1`) goes by that short name too while Docker + Hub's image of it is not here, so `docker run somos/app:1` finds it. - Volumes and networks: list, inspect, create, remove and prune; connect and disconnect with aliases and fixed addresses. `bridge`, `host` and `none` are the pre-defined networks. From 01cd8d275a6a1a450ed0556a80cce45f31047cbd Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Fri, 6 Nov 2026 21:01:22 -0500 Subject: [PATCH 3/3] Document info, images inspect, -e and build of 3.0.0 - reference.md: info (checked against its output with the 3.0.0 package), images inspect by name or reference and image as the same group, both among the reads that ask for org.nspawn.inspect; build --force and an mkosi build's --name; a bare -e VAR or --env-file KEY that is not set is left out, on create, start and exec, and a start that gives nothing keeps the remembered variables; compose prints its errors as they are. - building.md: a tag built again replaces the image of its reference while nothing ran from it, and goes beside a machine or an image of another reference that keeps the name. - machines.md: a bare -e VAR that is not set is left out. - overview.md: images inspect and info among the reads. - docker-api.md: /var/run/docker.sock by its path, the drop-in named symlink.conf, and no comparisons with docker. - contribution-guidelines.md: the preview with sudo nspawn compose up, as the README of this repository has it. --- content/en/docs/building.md | 16 +++++-- content/en/docs/contribution-guidelines.md | 8 ++-- content/en/docs/docker-api.md | 20 ++++---- content/en/docs/machines.md | 5 +- content/en/docs/overview.md | 2 +- content/en/docs/reference.md | 56 ++++++++++++++++++---- 6 files changed, 77 insertions(+), 30 deletions(-) diff --git a/content/en/docs/building.md b/content/en/docs/building.md index 6a538f9..87ec8bd 100644 --- a/content/en/docs/building.md +++ b/content/en/docs/building.md @@ -14,10 +14,18 @@ mkosi configuration. A directory with a `Dockerfile` (or `dockerfile`, Dockerfile flags below make it a Dockerfile build; a directory with `mkosi.conf` or `mkosi.conf.d` and no Dockerfile, or the mkosi flags, an mkosi build. Either result is stored like a pulled image and can be started right -away or pushed. A Dockerfile build of a tag replaces the image it built or -pulled under that reference before, as long as no machine ran from it; `--force` -replaces any other image of the name, an mkosi build's included. `-f` names the -Dockerfile, so `--force` has no short form. +away or pushed. A build of a tag replaces the image of that reference under the +tag's local name as long as nothing ran from it, whatever it was pulled or built +as: the tag moves to the new build, so a script that builds and runs its image +builds it again. A machine that has the name (one that ran, starts or runs), or +an image of another reference, keeps it, and the build goes beside it with the +same reference, into the image an earlier build of the tag put there or else +under a free name (`app-2`), which a note says; `run`, `create`, compose and the +Docker API find it by the reference, the image before the machine. An mkosi +build given `--name` is refused instead when a machine or an image of another +reference has that name. `--force` replaces whatever has the name, a machine +that ran included, and refuses one that runs. `-f` names the Dockerfile, so +`--force` has no short form. ## Dockerfiles diff --git a/content/en/docs/contribution-guidelines.md b/content/en/docs/contribution-guidelines.md index 6a58247..d5e1a15 100644 --- a/content/en/docs/contribution-guidelines.md +++ b/content/en/docs/contribution-guidelines.md @@ -73,17 +73,17 @@ sending it. ## Working on this site The site is built with [Hugo](https://gohugo.io/) and the -[Docsy](https://www.docsy.dev/) theme. With Docker installed, a live preview +[Docsy](https://www.docsy.dev/) theme. With nspawn installed, a live preview needs nothing else: ```shell git clone https://github.com/nspawn/nspawn.org.git cd nspawn.org -docker compose up +sudo nspawn compose up ``` -Then open . The container runs as uid and gid 1000; the +Then open . The machine runs as uid and gid 1000; the README of the repository explains how to pass yours if they differ. Without -Docker, install Node.js, Go and git and run `npm run install:safe` followed by +nspawn, install Node.js, Go and git and run `npm run install:safe` followed by `npm run serve`. The pages are Markdown files under `content/en/`; every documentation page has an "Edit this page" link that opens the file on GitHub. diff --git a/content/en/docs/docker-api.md b/content/en/docs/docker-api.md index f9abeaa..2d7a033 100644 --- a/content/en/docs/docker-api.md +++ b/content/en/docs/docker-api.md @@ -3,13 +3,13 @@ title: The Docker API weight: 8 description: >- The Docker Engine API on a socket of the host and in each machine that mounts - docker's socket, for the docker command, CI runners, reverse proxies and + /var/run/docker.sock, for the docker command, CI runners, reverse proxies and dashboards, and auto-update of the machines whose tag points to a newer image. --- nspawn serves the Docker Engine API, 1.51 and any version down to 1.24 a client -asks for, on a socket of the host and in each machine that mounts docker's -socket. Clients make, start, attach to, exec in, copy files to and from and +asks for, on a socket of the host and in each machine that mounts +`/var/run/docker.sock`. Clients make, start, attach to, exec in, copy files to and from and remove containers, pull and remove images, and make, join and remove volumes and networks. The server is a client of the [service](/docs/overview/#the-service), as the command line is: every change it makes is a method of `org.nspawn`, under @@ -76,12 +76,12 @@ $ docker run --rm -v data:/data busybox:1.37 cat /data/page hello ``` -On a host without docker, clients that only look at `/run/docker.sock` or -`/var/run/docker.sock` find the socket there with a drop-in: +Where nothing else on the host has `/run/docker.sock`, clients that only look +there or at `/var/run/docker.sock` find the socket with a drop-in: ```shell sudo mkdir -p /etc/systemd/system/nspawn-api.socket.d -printf '[Socket]\nSymlinks=/run/docker.sock\n' | sudo tee /etc/systemd/system/nspawn-api.socket.d/docker.conf +printf '[Socket]\nSymlinks=/run/docker.sock\n' | sudo tee /etc/systemd/system/nspawn-api.socket.d/symlink.conf sudo systemctl daemon-reload && sudo systemctl restart nspawn-api.socket ``` @@ -138,12 +138,12 @@ container's name in the API, and its machine gets one made of it (`reaper-1-` an eight hex digits), which `nspawn ps` shows. A network keeps the name the API gave it the same way. -## docker's socket in a machine +## The socket in a machine A machine that mounts `/var/run/docker.sock`, `/run/docker.sock` or -`/run/nspawn/api.sock` from the host gets nspawn's Docker API there instead of the -host's docker, with `-v /var/run/docker.sock:/var/run/docker.sock` or compose's -`use_api_socket`. Mounted read-write, it takes every request the API serves: +`/run/nspawn/api.sock` from the host gets nspawn's Docker API there, whatever the +host has at that path, with `-v /var/run/docker.sock:/var/run/docker.sock` or +compose's `use_api_socket`. Mounted read-write, it takes every request the API serves: ```text $ sudo nspawn run --rm -v /var/run/docker.sock:/var/run/docker.sock \ diff --git a/content/en/docs/machines.md b/content/en/docs/machines.md index b8f2063..c7191b4 100644 --- a/content/en/docs/machines.md +++ b/content/en/docs/machines.md @@ -238,7 +238,10 @@ say otherwise: `--image-command` forgets them and runs the image's own entrypoint and cmd again. - `-e VAR=value` adds a variable on top of the image's, and `-e VAR` copies it - from the shell that runs nspawn. `--env-file FILE` reads `KEY=VALUE` lines + from the shell that runs nspawn, or is left out when the shell does not have + it, so `-e DEBUG` passes an optional setting on only where it is set; a + `start` whose variables are all left out keeps the ones the machine + remembers. `--env-file FILE` reads `KEY=VALUE` lines (no quoting, no expansion, `#` comments), before the `-e` that follow. The later value of a variable wins, `-e none` forgets them all, and `exec` sees the same environment as the program. Any name but an empty diff --git a/content/en/docs/overview.md b/content/en/docs/overview.md index dad56b8..6a51e3c 100644 --- a/content/en/docs/overview.md +++ b/content/en/docs/overview.md @@ -98,7 +98,7 @@ two actions: | Action | Methods | | --- | --- | -| `org.nspawn.inspect` | The ones that only read: `images ls`, `ps` and `machines ls`, `inspect`, `generate`, `top`, `stats`, `events`, `wait`, `port`, `system df`, `builder du`, `network ls` and `network inspect`, `volume ls` and `volume inspect`, `secret ls` and `secret inspect`. | +| `org.nspawn.inspect` | The ones that only read: `images ls` and `images inspect`, `ps` and `machines ls`, `inspect`, `info`, `generate`, `top`, `stats`, `events`, `wait`, `port`, `system df`, `builder du`, `network ls` and `network inspect`, `volume ls` and `volume inspect`, `secret ls` and `secret inspect`. | | `org.nspawn.manage` | Everything else: pulling, building, starting, stopping, restarting, pausing, removing, `exec`, `shell`, `cp`, `logs`, volumes, secrets, the registry commands and the credentials. | Both are for administrators by default, so `sudo nspawn ...` works, and a diff --git a/content/en/docs/reference.md b/content/en/docs/reference.md index a2d0ae8..457e429 100644 --- a/content/en/docs/reference.md +++ b/content/en/docs/reference.md @@ -10,13 +10,13 @@ description: >- explains them in full. Errors are printed as `error: ...` on standard error and the exit status is 1 (2 for a command line clap refuses); `exec` and an attached `run` exit with the status of the program they ran, and `nspawn compose` prints -its errors as docker compose does. +its errors as they are, without `error:`. Every command but `daemon`, `completions` and the unit hooks is a call to the [service](/docs/overview/#the-service) on the system bus, which asks polkit whether the caller may take the action: the listings (`images ls`, `ps`, -`machines ls`, `inspect`, `generate`, `top`, `stats`, `events`, `wait`, -`port`, `system df`, `builder du`, `network ls`, `network inspect`, +`machines ls`, `inspect`, `images inspect`, `info`, `generate`, `top`, `stats`, +`events`, `wait`, `port`, `system df`, `builder du`, `network ls`, `network inspect`, `volume ls`, `secret ls`, `secret inspect`) ask for `org.nspawn.inspect`; everything else, `search` and `hub` included, asks for `org.nspawn.manage`. Both are for administrators by default, and root is never @@ -146,7 +146,7 @@ the image's own name, as `run` does. The layers are shared with the source. | `-p`, `--publish [IP:][HOST:]CONTAINER[/udp]` | Ports to publish on the host, like `start -p`. Not inherited from the source. | | `-P`, `--publish-all` | Publish each port the image exposes, like `start -P`. | | `--entrypoint PROGRAM` | Replace the image's entrypoint and drop its cmd, so only the arguments given follow it; an empty string runs the arguments alone. An app left with nothing to run is refused with `no command specified`. App images only. | -| `-e`, `--env VAR[=VALUE]` | Environment for the program, `VAR=value` or `VAR` copied from the calling shell. App images only. | +| `-e`, `--env VAR[=VALUE]` | Environment for the program, `VAR=value` or `VAR` copied from the calling shell, left out when the shell does not have it. App images only. | | `-v`, `--volume SOURCE:TARGET[:ro]` | Mount a host directory or a named volume; `z`, `Z`, a propagation, `nocopy` and `cached` are taken and do nothing. | | `-l`, `--label KEY=VALUE` | Label the machine, on top of the image's own labels. Not inherited from the source. | | `--restart`, `-m`, `--memory-swap`, `--cpus`, `--pids-limit` | Restart policy and limits, as for [start](#start). Not inherited from the source. | @@ -180,7 +180,7 @@ an mkosi option, an mkosi build, which needs mkosi. See | --- | --- | | `DIRECTORY` | The build context: a directory with a Dockerfile, or an mkosi configuration. `-` reads a context tar, or a Dockerfile with no context, from standard input. Default: `.`. | | `-t`, `--tag TAG` | Reference for the result, for example `myapp:1` or `hub.example/team/app:2`. Repeatable for a Dockerfile; an mkosi build takes one, and needs it. A Dockerfile build without one fills the cache and prints the image ID. | -| `--force` | Replace an existing image with the same name. | +| `--force` | Replace whatever has the image's name, a machine that ran included. Without it, an image of the tag's reference that nothing ran from gives way to the build; a machine, or an image of another reference, keeps the name, and the build goes beside it with the same reference (into the image an earlier build of the tag put there, or else a free name such as `app-2`), which a note says. | | `--backend`, `--mode` | As for `pull`. | | `-f`, `--file PATH` | The Dockerfile. Default: `Dockerfile`, `dockerfile` or `Containerfile` in the context; `-` reads it from standard input. | | `--build-arg KEY[=VALUE]` | A build argument; `KEY` alone takes it from the environment. Repeatable. | @@ -202,7 +202,7 @@ an mkosi option, an mkosi build, which needs mkosi. See | `--push` | Push the tags once built. | | `--load` | Taken: the image is local already. | | `--no-verify` | Pull the base images without verifying their signatures. | -| `-n`, `--name NAME` | mkosi: local image name. Default: derived from the tag. | +| `-n`, `--name NAME` | mkosi: local image name. Default: derived from the tag. A machine or an image of another reference that has it is refused without `--force`. | | `-d`, `--distribution DISTRIBUTION` | mkosi: distribution to build (`mkosi --distribution`). | | `-r`, `--release RELEASE` | mkosi: release to build (`mkosi --release`). | | `--profile PROFILE` | mkosi: profile to enable; repeatable. | @@ -232,7 +232,8 @@ terminal a bar shows how far each blob got, as with `pull`. ## images -Manage local images; without a subcommand, list them. +Manage local images; without a subcommand, list them. `image` is the same +group: `nspawn image ls`, `image rm`, `image prune` and `image inspect`. ### images ls @@ -247,6 +248,24 @@ reference, size and whether the image is read-only. `--json` prints the list as the service returns it. `images list` is an alias, and `images` without a subcommand prints the same list. +### images inspect + +```text +nspawn images inspect [-f FORMAT] NAME|REFERENCE... +``` + +Prints the records of images as a JSON array, the keys of +[inspect](#inspect) without the ones it adds for a machine (state, start +time, leader, health, published ports, exit code). A record's name is that +record; anything else is a reference, and the image it means is the one the +service resolves it to, as `run` and `create` do: `busybox:1.37` is the +configured registry's `busybox:1.37` when there is one here, +`docker.io/library/busybox:1.37` otherwise; an image goes before a machine made +from it, and a machine is shown when it alone holds the image. One that is not +here is said as `no local image of NAME` after the others are printed, and the +command exits with 1. `-f`, `--format FORMAT` prints a Go template over each +record, as for [inspect](#inspect). + ### images prune ```text @@ -335,6 +354,23 @@ line each (`{{.state}}`, `{{json .labels}}`, `{{index .addresses "front"}}`), with the functions `json`, `join`, `split`, `lower`, `upper`, `title`, `pad` and `truncate`; an empty one prints the JSON. +## info + +```text +nspawn info [-f FORMAT] +``` + +Prints the command's version, then what the service answers: its version, its +machines by state (a record pulled or built that never ran is an image, not a +machine), its images, the registry, the default storage backend +(`auto (overlay)` says what `auto` picks on this host), the default log driver, +where machines and state are kept, systemd's version and the host's kernel, OS, +architecture, CPUs, memory, name and machine ID. Exits with 1 and the error when +the service does not answer, which is how a script checks that nspawn is +usable. `-f json` (`--format json`) prints the answer on one line, and +`-f FORMAT` a Go template over its keys (`{{.version}}`, +`{{.machines.running}}`). + ## generate ```text @@ -389,8 +425,8 @@ Boots an image as a machine. Every option is remembered for the next start. | `-P`, `--publish-all` | Publish each TCP and UDP port the image exposes (`EXPOSE`) that no `-p` publishes to a free port of the host. `--dns-option` and `--expose` are taken with a warning that they are ignored. | | `--ip [NETWORK=]IP` | A fixed IPv4 address on the primary network, or on `NETWORK`: one made with [network create](#network-create), inside its subnet and nobody else's there. Repeatable, once per network. | | `--entrypoint PROGRAM` | Replace the image's entrypoint and drop its cmd, so only the arguments given follow it; an empty string runs the arguments alone. An app left with nothing to run is refused with `no command specified`. App images only. | -| `-e`, `--env VAR[=VALUE]` | Environment for the program, `VAR=value` or `VAR` copied from the calling shell. Repeatable; `none` forgets them. App images only. | -| `--env-file FILE` | Variables from a file of `KEY=VALUE` lines (no quoting, no expansion, `#` comments, a `KEY` alone copied from the calling shell), before the `-e` that follow and win. Repeatable. | +| `-e`, `--env VAR[=VALUE]` | Environment for the program, `VAR=value` or `VAR` copied from the calling shell, left out when the shell does not have it, so `-e DEBUG` passes an optional setting on only where it is set. Repeatable; `none` forgets them, and variables that are all left out keep the remembered ones. App images only. | +| `--env-file FILE` | Variables from a file of `KEY=VALUE` lines (no quoting, no expansion, `#` comments, a `KEY` alone copied from the calling shell or left out when it is not set there), before the `-e` that follow and win. Repeatable. | | `-v`, `--volume SOURCE:TARGET[:ro]` | Mount a host directory or a named volume (`z`, `Z`, a propagation, `nocopy` and `cached` are taken and do nothing), made on first use (or made ahead and still empty) with what the image has at `TARGET`, owner and files included. Repeatable; `none` forgets them. | | `--mount type=bind\|volume\|tmpfs,source=...,target=...` | The long form of `-v` and `--tmpfs`: `readonly`, `tmpfs-size`, `tmpfs-mode`; a volume needs its name, and image, cluster or npipe mounts, `volume-subpath` and driver options are refused. Repeatable. | | `-l`, `--label KEY=VALUE` | Label the machine, on top of the image's own labels. Repeatable; `none` forgets them. | @@ -584,7 +620,7 @@ status. A paused machine is refused at once. | Option | Meaning | | --- | --- | | `-u`, `--user USER[:GROUP]` | User inside the machine, a name or a number of its passwd file, with a group of its group file after a colon (then the only group; otherwise the user's supplementary groups come along) and the home of the passwd entry. Default: the machine's user, its `--user` or else its image's `USER`; root on a booted machine. | -| `-e`, `--env VAR[=VALUE]` | A variable for the command, `VAR=value` or `VAR` copied from the calling shell. Repeatable. | +| `-e`, `--env VAR[=VALUE]` | A variable for the command, `VAR=value` or `VAR` copied from the calling shell, left out when the shell does not have it. Repeatable. | | `-w`, `--workdir DIR` | Working directory of the command, instead of the machine's. | | `-T`, `--no-tty` | No terminal, even from one: pipes, as in a script. Without `-t` or `-T`, a terminal only when standard input and output are one, so redirected output is byte-exact. | | `-t`, `--tty` | A terminal for the command, even without one here. |