Guides
Docker host access from container
First you need this line in your service
extra_hosts:
- host.docker.internal:host-gateway
Second, anywhere you use the URI needs to have "host.docker.internal" rather than "localhost" or the IP.
Third, ufw needs to have access to the network subnet. To do this first check which network was created by compose for this using docker network ls
. Then use docker network inspect <name>
. After this take the subnet under IPAM and run the following. The host port is the port you're trying to forward from the localhost to the docker container.
sudo ufw allow from <subnet-w-cidr> to any port <host-port>
Expose container only via Tailscale
Because Docker deals with iptables straight rather than going through UFW, any firewall rules set wouldn't affect a docker mapped port. To ensure your Docker container is only accessible via Tailscale and not via the host's IP address, you can configure a Docker container to only listen on the Tailscale network interface.
Find the Tailscale IP Address
On the Docker host, run tailscale ip
to get the Tailscale IP address (e.g., 100.x.x.x
).
Modify Docker Daemon Configuration
Create or edit the Docker daemon configuration file, usually located at /etc/docker/daemon.json
.
Add the following configuration to bind Docker to the Tailscale network interface:
{ "hosts": ["fd://", "tcp://100.x.x.x:2375"] }
This binds Docker to the Tailscale IP on port 2375
(you can use any available port).
Override default Configuration in Systemd
Edit the Docker service configuration file and remove the hosts flag:
sudo systemctl edit docker
### Anything between...
[Service]
ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock
### Lines below...
Restart the Docker daemon to apply the changes
sudo systemctl restart docker
Run Docker Container Bound to Tailscale Interface
When you run your Docker container, bind it to the Tailscale IP and the desired port (e.g., 5001
):
docker run -p 100.x.x.x:5001:5001
your-docker-image
Now configure the firewall. Unless the default rule is deny (incoming)
block it using sudo ufw deny 5001
Compose Files
PHP Server
This is a simple PHP server with Nginx as a reverse proxy. The PHP server is running on port 9000 and Nginx is running on port 8080. The PHP server is using the php:8.2-fpm
image and the Nginx server is using the nginx:latest
image. The PHP server is mounted to the ./html/
directory and the Nginx server is mounted to the ./nginx/conf.d/
directory.
- docker-compose.yml
- index.php
- default.conf
version: "3"
name: php-server
services:
nginx:
image: nginx:latest
ports:
- "8080:80" # local:container
networks:
- internal
volumes:
- ./html/:/var/www/html/ # local:container
- ./nginx/conf.d/:/etc/nginx/conf.d/
php:
image: php:8.2-fpm
networks:
- internal
volumes:
- ./html/:/var/www/html/
server {
listen 0.0.0.0:80;
root /var/www/html;
location / {
index index.php index.html;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
}
}
MySQL Server
This is a simple MySQL server running on port 3306. The MySQL server is mounted to the ./mysql/
directory.
version: "3"
name: mysql-server
services:
db:
image: mysql:8.0
ports:
- "3306:3306" # local:container
networks:
- internal
volumes:
- ./mysql/:/var/lib/mysql/ # local:container
environment:
MYSQL_ROOT_PASSWORD: root # set the root password