systemd-nspawn: Lightweight Containers Without Docker
AI generated
$
/etc
Linux
systemd-nspawn: Lightweight Containers Without Docker
When the systemd native container tool is the better choice

Where Docker ships a full runtime complete with its own image format, registry integration, and orchestration, systemd-nspawn takes the opposite approach: a minimalist tool, deeply integrated into systemd, that turns a chroot-like directory or image into a fully isolated container within seconds, with no daemon, registry, or compose file involved.

10 min read Linux Containers systemd

1. When systemd-nspawn beats Docker

systemd-nspawn fits whenever you need a complete, isolated Linux system that behaves like a standalone machine, rather than a single process wrapped in a container shell. Unlike a typical Docker container, which runs exactly one main process, an nspawn container usually boots a full init system as PID 1, complete with its own logging, its own services, and its own network stack, making it feel closer to a very lightweight virtual machine than a classic application container.

Typical use cases include build and test environments for different distributions, isolated development sandboxes on a workstation, or minimalist hosting setups where you would rather not install an additional container runtime daemon, since systemd already runs on virtually every modern Linux server anyway. For microservice architectures with many independently deployable single processes, Docker or Podman remains the better fit most of the time, since the one process per container model and the rich image ecosystem offer clear advantages there.

2. Creating and starting a container from a directory

The simplest entry point is a container built directly from a local directory holding a complete root filesystem. Tools such as debootstrap for Debian based systems or dnf --installroot for RPM based distributions populate an empty directory with a bootable base system that then serves directly as the container root, with no prebuilt registry image involved at all.

The command systemd-nspawn -D starts that directory as a container with an interactive shell, while systemd-nspawn -bD instead triggers a full boot with an init system as PID 1, including systemd's own logging and service management inside the container. This boot mode is really the core of nspawn: afterward the container behaves in almost every respect like a standalone system, where services can be managed with systemctl status just as on a physical host.


# Install a minimal Debian system into a directory
sudo debootstrap bookworm /var/lib/machines/test-container

# Start the container interactively as a shell
sudo systemd-nspawn -D /var/lib/machines/test-container

# Full boot with an init system as PID 1
sudo systemd-nspawn -bD /var/lib/machines/test-container

3. Starting and importing a container from an image

Besides local directories, systemd-nspawn also supports ready made disk images in raw, qcow2, or tar format via the -i or --image= flag. Such images can be imported directly from an HTTP or HTTPS source using machinectl pull-tar or machinectl pull-raw, with systemd optionally verifying the image's GPG signature before storing it locally.

After importing, the image lands by default under /var/lib/machines/, the standard directory shared by systemd-nspawn and machinectl for containers. From there the container can be started directly by name with machinectl start, without having to specify the full path again, which makes day to day handling considerably more convenient than the plain directory approach.


# Import a ready made tar image, including GPG signature verification
machinectl pull-tar https://images.example.com/debian-12.tar.xz debian-base

# Start the imported container via machinectl
machinectl start debian-base
machinectl shell debian-base

4. Networking options: from shared network to bridge

Without explicit network configuration, an nspawn container shares the host's network namespace by default, which is simple but provides no network isolation whatsoever and invites port conflicts between host and container. For real isolation, the --network-veth flag enables a virtual ethernet pair between host and container, automatically creating a host side interface named after the pattern ve-.

For more production oriented setups, this veth interface can be bound directly to an existing bridge on the host via --network-bridge=, giving the container its own IP address on the same subnet as the host. Alternatively, --network-macvlan= lets the container participate directly on the physical network through its own virtual MAC address, which matters especially for containers that need to be reachable from outside via their own IP, with no NAT or port forwarding involved.


# Start a container with its own veth pair attached to a host bridge
sudo systemd-nspawn -bD /var/lib/machines/web-container \
  --network-bridge=br0

# Alternative: a dedicated virtual MAC address via macvlan
sudo systemd-nspawn -bD /var/lib/machines/web-container \
  --network-macvlan=eth0

5. machinectl integration for everyday operations

machinectl is the central management tool for nspawn containers and treats them as full fledged systemd machines, the same concept systemd also uses for virtual machines. Commands like machinectl list, machinectl status, and machinectl poweroff work regardless of whether the container was started manually with systemd-nspawn or through a registered systemd unit.

Especially handy is machinectl shell, which opens a login shell directly inside the container without having to set up SSH access, along with machinectl copy-to and machinectl copy-from for copying files between host and container through the machinectl interface. Containers meant to run permanently are best registered as a systemd unit via systemctl enable systemd-nspawn@, so machinectl and systemd jointly manage the lifecycle, including automatic startup at boot.


# List all registered containers
machinectl list

# Permanently enable a container as a systemd unit
sudo systemctl enable --now systemd-nspawn@web-container

# Direct shell access without SSH
machinectl shell web-container

6. Resource limits and depth of isolation compared

Since systemd-nspawn containers are managed as regular systemd units, the same resource limit mechanisms apply as for any other service: MemoryMax, CPUQuota, and IOWeight can be set directly in the generated unit file or at runtime via systemctl set-property, with no separate cgroup configuration outside the systemd ecosystem needed. That is an advantage over Docker, where resource limits are configured through dedicated CLI flags instead of native distribution tooling.

In terms of isolation depth, nspawn sits between a simple chroot and a full VM: it uses the same kernel namespaces as Docker, PID, mount, UTS, IPC, and optionally network, but by default skips the strict seccomp filtering Docker ships out of the box, though a custom seccomp profile can be added via --system-call-filter. For workloads with high security requirements, this flag should be set explicitly rather than relying on the comparatively open defaults.


# Set CPU and memory limits for a running container via systemctl
sudo systemctl set-property systemd-nspawn@web-container.service \
  CPUQuota=50% MemoryMax=512M

# Add a custom syscall filter set
sudo systemd-nspawn -bD /var/lib/machines/web-container \
  --system-call-filter=@basic-io --system-call-filter=@network-io

7. Bind mounts and persistent data

Unlike Docker with its explicit volume concept, nspawn relies on classic bind mounts via the --bind= flag, or --bind-ro= for read only mounts, to surface host directories inside the container. That feels more natural to administrators familiar with classic chroot or jail environments than Docker's own volume management, though it also brings less built in abstraction such as named volumes.

For persistent database or application data, it is best to maintain a separate directory outside the container's root filesystem and mount it in via bind mount, so that accidentally deleting or rebuilding the container never endangers the actual data. Because nspawn containers correspond to regular filesystem paths, backups can be performed with classic tools like rsync or tar, with no container specific tooling required.


# Mount a persistent data directory from the host into the container
sudo systemd-nspawn -bD /var/lib/machines/db-container \
  --bind=/srv/container-data/db:/var/lib/mysql

# Mount a read only configuration directory
sudo systemd-nspawn -bD /var/lib/machines/db-container \
  --bind-ro=/etc/container-config:/etc/app-config

8. Where it diverges from full Docker setups

A full Docker setup brings a rich image ecosystem, standardized compose definitions for multi container applications, and a broad tooling landscape for CI pipelines, registries, and orchestration. systemd-nspawn has none of that: no registry integration, no layered image format, and no built in multi container orchestration, which quickly turns into manual busywork for complex applications made up of many services.

The trade off pays off exactly where that Docker complexity is unwelcome: single, long lived system containers that should behave like a standalone lightweight VM, minimalist hosts without an extra runtime daemon, or test environments that need several distributions running in parallel as full booting systems. For a typical Magento stack made of several loosely coupled services, Docker or Podman remains the more pragmatic choice most of the time, while nspawn suits individual, isolated system services better, for example a dedicated build server per distribution.

9. Practical example: an isolated build sandbox for packaging

A common scenario in hosting environments is an isolated build sandbox where packages or PHP extensions get compiled for a specific distribution, without polluting the host with build dependencies. A minimal Debian directory created via debootstrap serves as a reusable base that can be reset from a clean snapshot before every build run, something a simple btrfs or overlayfs snapshot of the container directory accomplishes in seconds.

Because the build runs inside a fully booted system, even build processes that themselves depend on systemd services, such as a local database service for integration tests during the build run, work without extra contortions. Once the build finishes, the resulting artifact can simply be copied out of the container directory, while the container itself gets discarded afterward or reset to the clean snapshot.


# Boot the build sandbox from a clean snapshot, copy out the result afterward
sudo systemd-nspawn -bD /var/lib/machines/build-bookworm \
  --bind=/srv/build-output:/output

# After the build: reset the container directory to a clean state
sudo btrfs subvolume delete /var/lib/machines/build-bookworm
sudo btrfs subvolume snapshot /var/lib/machines/build-bookworm-clean \
  /var/lib/machines/build-bookworm
Criterion systemd-nspawn Docker
Base model full system with init as PID 1 one main process per container
Daemon no daemon, systemd manages the lifecycle dockerd as a permanent background service
Image format directory or tar/raw image, no registry layered OCI image with registry ecosystem
Resource limits native systemd unit properties dedicated CLI flags and cgroup wrapper
Typical use isolated system containers, build sandboxes microservices, multi container applications

Mironsoft

Server administration, Docker hosts, and performance tuning

Linux servers nobody on the team really understands anymore?

We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.

Server Audit

Review the existing server configuration for security gaps and performance bottlenecks.

Docker Host Setup

Set up and secure production-ready Docker environments for Magento cleanly.

Monitoring & Tuning

Measure resource usage and tune systemd, kernel, and services with purpose.

10. Summary

systemd-nspawn

Core idea

Full system with init as PID 1, no one process model

Management

machinectl and native systemd units, no separate daemon

Networking

veth, bridge, or macvlan depending on isolation needs

Ideal use

Build sandboxes and isolated system services, not microservices

11. FAQ: systemd-nspawn

1Is systemd-nspawn a replacement for Docker?
Not across the board. For individual, system like containers, nspawn is often the lighter choice, while for microservice architectures with many loosely coupled services and an image ecosystem, Docker or Podman usually remains the better fit.
2Does systemd-nspawn need root rights?
Yes, in the default configuration root rights are required for namespace operations and network configuration. A restricted, unprivileged mode is possible but considerably more complex than Podman's rootless mode.
3How does an nspawn container differ from a virtual machine?
An nspawn container shares the kernel with the host and uses namespaces instead of full hardware virtualization. It behaves like a standalone system as a result, but stays noticeably lighter than a VM with its own kernel.
4Can I use Docker images with systemd-nspawn?
Not directly, since nspawn does not understand the layered OCI image format. A Docker image would first need to be exported and unpacked into a flat root filesystem directory before nspawn could use it as a container root.
5How do I start an nspawn container automatically at boot?
Via systemctl enable systemd-nspawn@containername, the container gets registered as a regular systemd unit and then starts automatically at boot like any other service, including restart logic after a crash.
6Which networking option gives a container its own IP?
For an own IP on the same subnet as the host, --network-bridge combined with an existing bridge interface fits best. For a dedicated MAC address with direct participation on the physical network, --network-macvlan is the right choice.
7How do I set CPU and memory limits for an nspawn container?
Since nspawn containers run as systemd units, limits work through systemctl set-property with the regular CPUQuota and MemoryMax properties, exactly as with any other systemd service.
8Is systemd-nspawn isolated as securely as Docker?
The namespaces used are similar, but nspawn's default configuration skips as strict a seccomp profile as Docker ships by default. For security critical workloads, a custom filter set should be added via --system-call-filter.
9What is machinectl actually used for?
machinectl manages nspawn containers as systemd machines and offers commands like list, status, shell, copy-to, and copy-from for daily handling, regardless of whether the container was started manually or as a unit.
10Is systemd-nspawn suitable for a production Magento stack?
For individual, isolated system services, certainly, but for a typical stack of several loosely coupled services like PHP FPM, Nginx, and Redis, Docker or Podman is usually the more pragmatic choice thanks to the richer image ecosystem and multi container orchestration.