Linux network namespaces let you run multiple WordPress instances on the same physical server with completely isolated network stacks — each namespace has its own loopback, virtual interfaces, routing table, and iptables rules. Combined with veth pairs and a bridge device, you get per-site firewall policies and the ability to run multiple MySQL instances on the same port number without conflicts, all without containers.
Problem: A WordPress Multisite network hosts sites for different clients on the same server — one site's PHP process or Nginx worker can see the filesystem and environment variables of another site, creating an isolation gap.
Solution: Use Linux network namespaces and systemd unit files to give each site's PHP-FPM pool its own network namespace, preventing direct TCP connections between pools. Combine with filesystem namespaces via PrivateTmp=yes and ProtectSystem=strict in the systemd unit to prevent cross-site file access. Use separate system users per site for DAC isolation.
The commands below create a network namespace for an isolated WordPress site, add a veth pair to connect it to the host, assign IPs, and set up iptables NAT so the namespace can reach the internet while remaining isolated from other namespaces.
# 1. Create a network namespace for site "site-a"
ip netns add site-a
# 2. Create a virtual ethernet pair (veth0 in host, veth1 in namespace)
ip link add veth0-site-a type veth peer name veth1-site-a
# 3. Move one end into the namespace
ip link set veth1-site-a netns site-a
# 4. Configure host-side interface
ip addr add 10.10.1.1/30 dev veth0-site-a
ip link set veth0-site-a up
# 5. Configure namespace-side interface
ip netns exec site-a ip addr add 10.10.1.2/30 dev veth1-site-a
ip netns exec site-a ip link set veth1-site-a up
ip netns exec site-a ip link set lo up
# 6. Add default route inside the namespace (via host-side IP)
ip netns exec site-a ip route add default via 10.10.1.1
# 7. Enable NAT on the host so the namespace can reach the internet
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 10.10.1.0/30 -j MASQUERADE
# 8. Run MySQL on port 3306 inside the namespace (isolated from host MySQL)
ip netns exec site-a mysqld \
--datadir=/var/lib/mysql-site-a \
--socket=/var/run/mysql-site-a/mysql.sock \
--port=3306 &
# 9. Verify isolation — host cannot reach namespace's MySQL port directly
# (must go through the veth bridge at 10.10.1.2:3306)
ip netns exec site-a ss -tlnp | grep 3306
# 10. Persist namespace with systemd (survives reboots)
# /etc/systemd/system/netns-site-a.service
# [Unit] Description=Network namespace site-a
# [Service] Type=oneshot RemainAfterExit=yes
# ExecStart=/usr/sbin/ip netns add site-a
# ExecStop=/usr/sbin/ip netns del site-a
# [Install] WantedBy=multi-user.target
NOTE: Network namespaces are a lower-level primitive than Docker containers; for most WordPress multi-site isolation needs, a Docker Compose setup with one container per site is easier to manage and reproduce — raw network namespaces are most useful when you need to add isolation to an existing bare-metal server without migrating to containers.