Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/en/docs/compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ $ sudo nspawn compose up -d
$ sudo nspawn compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
shop-db-1 postgres:18-alpine "docker-entrypoint.s…" db 4 seconds ago Up 3 seconds (healthy)
shop-web-1 nginx:alpine "/docker-entrypoint.…" web 3 seconds ago Up Less than a second 0.0.0.0:8080->80/tcp
shop-web-1 nginx:alpine "/docker-entrypoint.…" web 3 seconds ago Up Less than a second 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp

$ sudo nspawn compose exec db psql -U postgres -c 'select 1'
?column?
Expand Down
5 changes: 3 additions & 2 deletions content/en/docs/machines.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ variables and the volumes of the last run:
fixes its address on one made with `network create`; see
[Networking](/docs/networking/).
- `-p [IP:]HOST:CONTAINER[/udp]` publishes a port on the host, on every
address or on one, like docker. It needs a bridge network; `-p none` forgets
all published ports.
IPv4 and IPv6 address or on one, like docker. It needs a bridge network;
`-p none` forgets all published ports.
- `--entrypoint`, `-e`, `--env-file` and the arguments after `--` change what
an **app** image runs; see [Command, entrypoint and environment](#command-entrypoint-and-environment).
Boot images refuse them.
Expand Down Expand Up @@ -909,6 +909,7 @@ $ sudo nspawn wait job
3
$ sudo nspawn port web
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
```

`wait NAME...` waits for machines to stop and prints each one's exit code, and
Expand Down
51 changes: 43 additions & 8 deletions content/en/docs/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ host's address on the machine's network. On hosts with systemd 258 or newer,
machined also lets the host resolve machine names by itself. The bridge
carries IPv4 only, so neither the machines nor the bridge get an IPv6
link-local address, and a machine's name leads to its bridge address:
`ping web` from the host answers from `10.99.0.x`.
`ping web` from the host answers from `10.99.0.x`. Published ports answer
over IPv6 all the same; see [IPv6](#ipv6).

## Networks of your own

Expand Down Expand Up @@ -221,11 +222,11 @@ sudo nspawn network inspect front
sudo nspawn start web -p 8080:80 -p 5353:53/udp -p 127.0.0.1:9090:9090 -p 8000-8010:8000-8010
```

Each `-p [IP:]HOST:CONTAINER[/udp]` becomes a DNAT entry in the `ip nspawn`
table. Without an address the port is reachable from other hosts, from the
host's own addresses and from `127.0.0.1` (through `route_localnet`, as docker
does without its userland proxy); with one, `127.0.0.1:9090:9090` say, on that
address of the host alone, for a reverse proxy in front. A range,
On IPv4 each `-p [IP:]HOST:CONTAINER[/udp]` becomes a DNAT entry in the `ip
nspawn` table. Without an address the port is reachable from other hosts, from
the host's own addresses and from `127.0.0.1` (through `route_localnet`, as
docker does without its userland proxy); with one, `127.0.0.1:9090:9090` say, on
that address of the host alone, for a reverse proxy in front. A range,
`8000-8010:8000-8010`, is one mapping per port, which `inspect` shows one by
one. The entries are installed once the machine is registered and removed when
it ends, by the unit hooks, so they also go away after a crash or when the
Expand All @@ -239,12 +240,46 @@ the same ports, and `-p none` forgets them all. Ports need a bridge network
(not an internal one); a machine on the host's network listens on the host's
ports directly.

### IPv6

The machines have IPv4 addresses alone, and nftables cannot translate IPv6 into
IPv4, so a port answers on the host's IPv6 addresses through a proxy, as
docker's userland proxy serves it. `nspawn-proxy-NAME.service`, a transient
unit the machine's hooks start once it runs and stop with it, listens there and
relays TCP connections and UDP datagrams to the machine's address; UDP replies
leave from the address the client wrote to, so HTTP/3 works too. The IP of `-p`
says where a port is published, as with docker:

| `-p` | Where |
| --- | --- |
| `8080:80` | every IPv4 address and, on a host with IPv6, every IPv6 address |
| `0.0.0.0:8080:80` | every IPv4 address |
| `127.0.0.1:8080:80` | that IPv4 address |
| `[::]:8080:80` | every IPv6 address |
| `[2001:db8::1]:8080:80`, `[::1]:8080:80` | that IPv6 address |

```text
$ sudo nspawn port web
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
```

A link-local address (`fe80::`) cannot be named, as it needs an interface;
`[::]` takes it with the others. The port checks cover the IPv6 side as well,
and when the proxy cannot listen, `start` says so and the IPv4 side works all
the same (`journalctl -u nspawn-proxy-web` says why). A machine sees an IPv6
client as the bridge's gateway address, not the client's own, as with docker's
proxy. The client comes in through the host's input path, not its forwarding,
so a host firewall has to let the port in, as for any service of the host;
nspawn changes no rule for it.

## Firewalls

- **firewalld**: the bridge is placed in the `trusted` zone at runtime, which
also lets published ports through. The binding does not survive
also lets published ports through on IPv4. The binding does not survive
`firewall-cmd --reload`; the next `start` or `nspawn network up` puts it
back.
back. The IPv6 side of a port needs the port open in the zone of the host's
interface, as a service of the host does.
- **docker** (in its default iptables mode) and **ufw** set the `FORWARD`
policy to `DROP`, which would silence every machine on the bridge. `start`
then adds two rules to the `DOCKER-USER` chain, which docker reserves for
Expand Down
7 changes: 4 additions & 3 deletions content/en/docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,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, like `docker run --network container:NAME` (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`, like `docker --network-alias`: every member of that network resolves it. Repeatable; `none` forgets them. |
| `-p`, `--publish [IP:]HOST:CONTAINER[/udp]` | Publish a port on the host, like `docker -p`: on every address of the host, or on `IP` alone (`127.0.0.1:8080:80`). `8000-8010:8000-8010` publishes a range, one mapping per port. Repeatable; `none` forgets them all. |
| `-p`, `--publish [IP:]HOST:CONTAINER[/udp]` | Publish a port on the host, like `docker -p`: 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. `8000-8010:8000-8010` publishes a range, one mapping per port. Repeatable; `none` forgets them all. |
| `--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, like `docker --ip`. 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. |
Expand Down Expand Up @@ -623,8 +623,9 @@ nspawn port NAME [PRIVATE_PORT[/PROTO]]
```

Prints the ports a running machine publishes, like `docker port`
(`80/tcp -> 0.0.0.0:8080`), or only those of one port of the machine; one it
does not publish is an error, as docker words it.
(`80/tcp -> 0.0.0.0:8080`, and `80/tcp -> [::]:8080` on a host with IPv6), or
only those of one port of the machine; one it does not publish is an error, as
docker words it.

## cp

Expand Down
2 changes: 1 addition & 1 deletion hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ params:
privacy_policy: /about/#privacy

# Version of nspawn the documentation describes.
version: 2.0.0
version: 2.1.0
archived_version: false

# In-page links to open issues and suggest changes.
Expand Down