When running a web server inside a Docker container that listens on a Unix socket rather than a TCP port, you'll encounter permission and filesystem mapping challenges when trying to make that socket accessible to the host system. The standard -v
volume mounting approach often falls short due to permission issues and Docker's volume management.
The key is to use a bind mount instead of a named volume, as this gives you direct control over the socket file's location and permissions:
version: '3'
services:
web:
build: .
volumes:
- /path/on/host:/path/in/container
command: "your-server-command --socket /path/in/container/app.sock"
Your nginx configuration is close, but needs adjustment for proper socket communication:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://unix:/path/on/host/app.sock:;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Unix sockets require careful permission handling:
- Ensure the container user has write permissions to the socket directory
- The host's nginx worker process needs read/write access
- Consider using the same UID/GID in container and host
Here's a complete docker-compose example with uWSGI:
version: '3'
services:
app:
image: python:3.9
volumes:
- ./socket:/app/socket
command: >
sh -c "pip install uwsgi &&
uwsgi --socket /app/socket/app.sock
--chmod-socket=666
--module wsgi:app"
If you encounter connection problems:
- Verify socket exists:
ls -l /path/on/host/app.sock
- Check permissions:
stat -c "%a %U:%G" /path/on/host/app.sock
- Test socket connectivity:
curl --unix-socket /path/on/host/app.sock http://localhost
When running web servers in Docker containers, we typically expose TCP ports for host access. However, many modern applications (like uWSGI, Gunicorn, or Node.js servers) prefer UNIX domain sockets for inter-process communication due to their performance benefits and security advantages.
While you might think simply volume-mounting the socket directory would suffice, Docker's volume behavior with sockets requires special handling:
# This naive approach often fails due to permission issues
volumes:
- /host/path:/container/path
Here's a working docker-compose.yml configuration that properly shares the socket:
version: '3.8'
services:
app:
build: .
volumes:
- socket_volume:/app/sockets
- ./app:/app
user: "1000:1000" # Match host user permissions
command: ["uwsgi", "--socket", "/app/sockets/app.sock", "--module", "app.wsgi"]
volumes:
socket_volume:
driver: local
driver_opts:
type: none
o: bind
device: /tmp/app_sockets # Host directory for the socket
The corresponding Nginx configuration should reference the host-mounted socket path:
server {
listen 80;
server_name example.com;
location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/app_sockets/app.sock;
uwsgi_read_timeout 300s;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Socket permissions are crucial for this to work:
# On the host system, ensure proper permissions
sudo mkdir -p /tmp/app_sockets
sudo chown -R $USER:$USER /tmp/app_sockets
sudo chmod -R 755 /tmp/app_sockets
If connections fail, use these diagnostic commands:
# Check if socket exists
ls -la /tmp/app_sockets/app.sock
# Test socket connectivity from host
socat - UNIX-CONNECT:/tmp/app_sockets/app.sock
# Check container logs
docker-compose logs app
For simpler setups, you can directly bind mount the socket directory:
version: '3'
services:
app:
volumes:
- /tmp/app_sockets:/app/sockets
# Rest of configuration...