Veil: Route Container Traffic Through a Proxy Automatically
I had containers on my server that needed a proxy to access the network. At first I installed proxy clients in every container, set environment variables, mapped ports. After a few machines it got old. Then it hit me: instead of configuring each container separately, just build a shared proxy network. Any container that joins it automatically routes through the proxy, no configuration needed. The project is open source at github.com/therainisme/veil
How it works
Two containers: veil-net runs Mihomo with TUN mode enabled, veil-box is the work environment. The key is a single line of config:
network_mode: "service:veil-net"
This makes veil-box share veil-net’s network stack. All traffic goes through Mihomo’s TUN virtual interface, and DNS is hijacked to 127.0.0.1:53 and handled by Mihomo. Programs inside the container have no idea a proxy exists. They just connect and resolve names as usual.
TUN was chosen because it’s the least hassle. HTTP/SOCKS proxies need per-application configuration, and many CLI tools don’t support them. Redir mode only covers TCP and can’t handle UDP traffic like DNS queries. TUN intercepts everything at the kernel level: TCP, UDP, ICMP, all of it. Truly transparent.
Usage
cp veil.yml.example veil.yml
# Edit veil.yml and fill in your proxy server info
vim veil.yml
docker compose up -d
docker exec -it -u veil veil-box bash
Inside the container, just curl google.com to test. No http_proxy needed, no application config changes.
The veil.yml.example template is already in the repo with DNS settings and routing rules ready to go.
Two versions
- veil-box: A daily development environment with git, vim, python3, build-essential and other common tools
- veil-actions-runner: Based on veil-box with Docker CE added on top, for running CI/CD pipelines that need a proxy
The corresponding compose files are docker-compose.yml and docker-compose.yml.actions-runner.
Making your own containers use the proxy
Not limited to veil-box. Any container can join this proxy network:
services:
veil-net:
# ... unchanged
my-app:
image: my-app:latest
network_mode: "service:veil-net"
depends_on:
- veil-net
A few notes
veil-net needs /dev/net/tun and NET_ADMIN/NET_RAW capabilities. TUN mode creates a virtual network interface at the kernel level. Without these permissions it won’t start. They’re already in docker-compose.yml:
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
veil-box runs systemd, which is why it’s in privileged mode. If you don’t need that, you can just run veil-net and connect your own container with network_mode: "service:veil-net". veil-box itself is optional.
Traffic that should go direct is going through the proxy? Check the rules section in veil.yml and make sure these direct rules exist: GEOSITE,CN,DIRECT and GEOIP,CN,DIRECT,no-resolve. Also make sure geodata-mode and geo-auto-update are both true.
After updating proxy config, just restart the proxy container:
docker compose restart veil-net
Update images:
docker compose pull
docker compose up -d