sdme
github.com/fiorix/sdme

Multi-Container Pod Networking

Share a network namespace between containers so they communicate via localhost.

Pods let multiple containers share a network namespace, so they can communicate over 127.0.0.1 without any port forwarding or bridge configuration. This is the same model used by Kubernetes pods.

See also the architecture documentation for implementation details.

Creating a pod

sudo sdme pod new mypod

This creates an isolated network namespace with only a loopback interface. Containers that join the pod share this namespace and can reach each other on localhost.

List and remove pods:

sudo sdme pod ls
sudo sdme pod rm mypod
sudo sdme pod rm -a       # remove all pods
sudo sdme pod rm -a -f    # force: also stop and remove attached containers

Example: nginx and curl via --pod

The --pod flag puts the entire container into the pod's network namespace. All processes inside the container share it.

Create a pod and two containers. We use the OCI nginx rootfs (from the OCI tutorial) since pod containers only have loopback and cannot download packages.

sudo sdme pod new webpod

Start an nginx server in the pod:

sudo sdme create http-server -r nginx --pod webpod --started

Start a client in the same pod and drop into a shell. The client uses a host clone which already has curl:

sudo sdme new http-client --pod webpod

Inside the client, curl the server via localhost:

curl localhost

You should see the nginx welcome page. Both containers share the same loopback interface.

You can see both containers and their pod association with:

sudo sdme ps

The POD column shows which pod each container belongs to.

Limitations of --pod

Pod network namespaces start with only a loopback interface. Without external networking, containers in a pod cannot reach the internet, download packages, or communicate with anything outside the pod.

To add external connectivity, use sdme pod net attach:

sudo sdme pod net attach webpod veth

This creates a veth pair between the pod and the host. The host's systemd-networkd handles DHCP and NAT automatically. See the external connectivity section below.

Without external networking, install any software you need before joining the pod, or use pre-built OCI rootfs images.

--pod works with --userns, --hardened, and --strict. The container is launched via nsenter --net= so the pod's network namespace is entered before nspawn creates the user namespace.

Example: redis via --oci-pod

The --oci-pod flag is for OCI application containers. Only the OCI app service process enters the pod's network namespace; the container's init and other services remain in their own namespace.

This works with --hardened and --strict because the network namespace join happens inside the container's own namespace.

Create a pod and import redis:

sudo sdme pod new dbpod
sudo sdme fs import redis docker.io/redis --base-fs ubuntu

Start redis in the pod with hardened security:

sudo sdme create redis-server -r redis --oci-pod dbpod --hardened --started

Verify redis is running:

sudo sdme logs redis-server --oci

Start a client container in the same pod using the same redis rootfs, so redis-cli is available. Both containers use --oci-pod with --hardened:

sudo sdme create redis-client -r redis --oci-pod dbpod --hardened --started

Test the connection from the client:

sudo sdme exec redis-client --oci -- redis-cli ping

You should see PONG. Both containers share the pod's network namespace and communicate over localhost, each running with hardened security.

--pod vs --oci-pod

Feature            --pod                  --oci-pod
-----------------  ---------------------  -------------------------
Scope              Entire container       OCI app service only
Container types    Any                    OCI app rootfs required
Userns/hardened    Compatible             Compatible
Requires           (nothing extra)        --private-network or
                                          --hardened/--strict
Use case           General-purpose pods   Security-hardened apps

Both flags can be used on different containers in the same pod. The pod's network namespace is shared regardless of which flag each container uses to join it.

External connectivity

Pods start with loopback-only networking. Use pod net attach to give containers in a pod internet access.

Requires: iproute2 (ip), dhcpcd.

Veth mode

Creates a point-to-point veth between the pod and the host:

sudo sdme pod new mypod
sudo sdme new mybox --pod mypod
sudo sdme start mybox
sudo sdme pod net attach mypod veth

Test connectivity from inside the container:

sudo sdme exec mybox -- ping -c1 8.8.8.8

Detach when no longer needed:

sudo sdme pod net detach mypod

Zone mode

Connects the pod to a named zone bridge. Multiple pods and regular containers on the same zone can reach each other directly:

sudo sdme pod new pod1
sudo sdme pod new pod2
sudo sdme pod net attach pod1 zone myzone
sudo sdme pod net attach pod2 zone myzone

Containers in pod1 and pod2 can now communicate through the vz-myzone bridge, and both have internet access.

How it works

The host's systemd-networkd handles everything: DHCP serving, NAT (IPMasquerade), and IP forwarding. sdme creates the veth pair with interface names that match nspawn conventions (ve-*, vb-*, vz-*), so networkd's default configs apply automatically.

A host-managed systemd service (sdme-pod-net@{pod}.service) runs dhcpcd inside the pod's netns using NetworkNamespacePath=. The host's systemd manages the DHCP client lifecycle.

DNS servers from the DHCP lease are extracted and written to each container's /etc/resolv.conf (for --pod) or each OCI app's chroot resolv.conf (for --oci-pod). Running containers are updated immediately on attach and detach.

Attach and detach are live operations: running containers see the interface appear or disappear immediately. Both --pod and --oci-pod containers work because they share the same netns.

After a reboot, networking is automatically restored when the first container referencing the pod starts.

Listing pod networking

sudo sdme pod ls

The table shows each pod's name, whether its netns is active, networking mode (NET), zone name, IP addresses, and creation time. Pods without external networking show - in the NET and ADDRESSES columns. Use --json or --json-pretty for structured output including DNS servers and container membership.

Zone interop with regular containers

Pods using zone mode share the same bridge as regular containers using --network-zone. They can reach each other directly:

sudo sdme pod new mypod
sudo sdme pod net attach mypod zone myzone
sudo sdme new mybox --pod mypod

sudo sdme new standalone -r ubuntu --network-zone=myzone

Both mybox (in the pod) and standalone (regular container) are on the vz-myzone bridge and can communicate.

Creating a pod with networking

Use --attach to combine pod creation and network attachment:

sudo sdme pod new mypod --attach veth
sudo sdme pod new mypod --attach zone --zone myzone