Securely expose your Nginx web server using the power of Tailscale β a zero-config VPN built on WireGuard. This guide walks you through:
- π Configuring HTTPS with Tailscale-generated certificates
- π Mapping host ports to your containerized Nginx instance
- π‘οΈ Restricting access to your private tailnet only
Whether you're self-hosting a dashboard, API, or static site, this setup ensures encrypted, authenticated access across your trusted devices β without opening public ports.
π¦ Built for Docker environments.
π§© Compatible with Alpine-based Nginx images.
π§ Designed for simplicity and security.
Run these commands to create the folder where Nginx will live.
mkdir proxy
cd proxy
Run these commands to creates folder structure
mkdir /html
mkdir -p /etc/nginx/conf.d
mkdir -p /etc/ssl/certs
Go to certs folder
cd /etc/ssl/certs
Run tailscale command to generate ssl certificate for your domain machine
sudo tailscale cert machineName.tailID.ts.net
At Below files, you need replace this domain for your domain's : machineName.tailID.ts.net
This is the docker compose file that creates a nginx container, save it inside proxy folder. /proxy/docker-compose.yml
version: "3.9"
networks:
nginx-net:
driver: bridge
services:
nginx:
image: nginx:stable-alpine3.21
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./html/index.html:/usr/share/nginx/html/index.html
- ./etc/nginx/conf.d:/etc/nginx/conf.d
- ./etc/nginx/default.conf:/etc/nginx/default.conf
- ./etc/ssl/certs:/etc/ssl/certs
Put this file at /etc/nginx/default.conf
user nginx;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# Zonas de limite (opcional)
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
# Tamanho do nome de dominio
server_names_hash_bucket_size 256;
# Incluir sites
include /conf.d/*.conf;
}
Put this file at /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name machineName.tailID.ts.net;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ =404;
}
}
server {
listen 443 ssl;
server_name machineName.tailID.ts.net;
# Setup valid tls versions
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'HIGH:!aNULL:!MD5';
ssl_prefer_server_ciphers off;
## Access and error logs.
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log info;
## Keep alive timeout set to a greater value for SSL/TLS.
keepalive_timeout 75 75;
ssl_certificate /etc/ssl/certs/machineName.tailID.ts.net.crt;
ssl_certificate_key /etc/ssl/certs/machineName.tailID.ts.net.key;
ssl_session_timeout 5m;
## Strict Transport Security header for enhanced security. See
## http://www.chromium.org/sts. I've set it to 2 hours; set it to
## whichever age you want.
add_header Strict-Transport-Security "max-age=7200";
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ =404;
}
}
At proxy folder's
sudo tailscale up
docker compose up -d
curl -vk machineName.tailID.ts.net
If you found this documentation useful, please consider supporting our work by becoming a sponsor.
- β PayPal
- π³ MercadoPago
- [βΏ Bitcoin] Or send a satoshi for us at this wallet address bc1q6j9ca23ekw8t5px30lg9t8qds6yqrhazhp0y99