How to Run Systemd Units in a Pre-existing Network Namespace: A Step-by-Step Guide


4 views

When working with containerized environments or network isolation scenarios, you might need to launch systemd services within specific network namespaces. The standard systemd configuration doesn't directly expose network namespace control, requiring some clever workarounds.

We'll need to combine several Linux features:

  • ip netns for namespace management
  • systemd-run for temporary service execution
  • nsenter for namespace entry

The most reliable method involves creating a wrapper script that enters the namespace before executing the actual service. Here's how to implement it:

1. Create the Network Namespace

sudo ip netns add mynetns
sudo ip link add veth0 type veth peer name veth1
sudo ip link set veth1 netns mynetns
sudo ip netns exec mynetns ip addr add 192.168.1.100/24 dev veth1
sudo ip netns exec mynetns ip link set veth1 up

2. Create a Systemd Service File

Save this as /etc/systemd/system/netns-service@.service:

[Unit]
Description=Service in network namespace %I

[Service]
ExecStart=/usr/local/bin/ns-wrapper %i /path/to/your/command
Restart=always
Type=simple

3. Create the Namespace Wrapper Script

Save this as /usr/local/bin/ns-wrapper:

#!/bin/bash
NETNS=$1
shift
exec ip netns exec $NETNS "$@"

Make it executable:

chmod +x /usr/local/bin/ns-wrapper

For more comprehensive namespace control, consider systemd-nspawn:

systemd-nspawn --network-namespace-path=/var/run/netns/mynetns \
  --bind=/usr/bin/myapp \
  /usr/bin/myapp --arguments

Verify the service runs in the correct namespace:

sudo systemctl start netns-service@mynetns
sudo ip netns exec mynetns ps aux | grep yourcommand
  • Namespace persistence: Ensure your namespace exists before service start
  • CAP_NET_ADMIN capabilities required
  • Network interfaces must be properly configured in the namespace
  • Consider using PrivateNetwork=yes in systemd units for additional isolation

When working with Linux network namespaces and systemd, you might encounter scenarios where you need to launch services in isolated network environments. This becomes particularly important when:

  • Running multiple instances of the same service with different network configurations
  • Isolating services for security or testing purposes
  • Creating complex network topologies for microservices

Before proceeding, ensure you have:

# Check systemd version
systemctl --version

# Verify iproute2 tools are installed
ip netns list

The most robust method involves using systemd-nspawn combined with namespace configuration. Here's how to implement it:

1. Creating the Network Namespace

# Create persistent network namespace
sudo ip netns add mynetns

# Verify creation
sudo ip netns list

2. Configuring the systemd Unit

Create a service file at /etc/systemd/system/myservice.service:

[Unit]
Description=My Service in Network Namespace
After=network.target

[Service]
ExecStartPre=/usr/bin/ip netns exec mynetns /bin/ip link set lo up
ExecStart=/usr/bin/ip netns exec mynetns /usr/bin/my-service-binary
Restart=on-failure
PrivateNetwork=yes

[Install]
WantedBy=multi-user.target

3. Alternative Using systemd-nspawn

For more complex scenarios, consider this approach:

# Create a dedicated nspawn configuration
sudo mkdir -p /etc/systemd/nspawn
sudo tee /etc/systemd/nspawn/mynetns.nspawn <<EOF
[Network]
VirtualEthernet=no
NetworkNamespace=/var/run/netns/mynetns
EOF

After implementing either solution:

# Reload systemd
sudo systemctl daemon-reload

# Start the service
sudo systemctl start myservice

# Verify namespace
sudo ip netns pids mynetns | xargs -L1 ps -fp
  • Ensure the network namespace exists before service start
  • Check journal logs with journalctl -u myservice -f
  • Verify capabilities if encountering permission issues
  • Consider using BusName= in unit files for D-Bus services

For production environments, you might want to:

# Add these to your service file
RestartSec=5s
StartLimitInterval=60
StartLimitBurst=5
PrivateTmp=yes
ProtectSystem=full