Skip to content

Instantly share code, notes, and snippets.

@timching
Created December 5, 2025 20:04
Show Gist options
  • Select an option

  • Save timching/367245bcfebd5ca5903b9766877d92f9 to your computer and use it in GitHub Desktop.

Select an option

Save timching/367245bcfebd5ca5903b9766877d92f9 to your computer and use it in GitHub Desktop.
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8
# Install toolset
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
iproute2 iputils-ping curl ca-certificates bash procps iptables dnsutils \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://tailscale.com/install.sh | sh
# Create a single-entrypoint script inside the image (no extra files required)
RUN mkdir -p /usr/local/bin \
&& tee /usr/local/bin/entrypoint.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
if [ "$AUTO_START" = "1" ]; then
tailscaled >/var/log/tailscaled.log 2>&1 &
fi
# Exec the requested command (default: bash). Network namespace is shared with the running openvpn process.
exec "$@"
EOF
RUN chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["bash"]
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8
# Install OpenVPN and small toolset
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
openvpn iproute2 iputils-ping curl ca-certificates bash procps iptables dnsutils \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /etc/openvpn
# Create a single-entrypoint script inside the image (no extra files required)
RUN mkdir -p /usr/local/bin \
&& tee /usr/local/bin/entrypoint.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Configurable paths (you typically mount your .ovpn into /etc/openvpn/client.ovpn)
OVPN_CONF="${OVPN_CONF:-/etc/openvpn/client.ovpn}"
OVPN_CREDS="${OVPN_CREDS:-/etc/openvpn/creds.txt}"
AUTO_START="${AUTO_START:-1}"
clean_up() {
if [ -n "${OPENVPN_PID:-}" ] && kill -0 "$OPENVPN_PID" 2>/dev/null; then
echo "Stopping OpenVPN (pid $OPENVPN_PID)..."
kill "$OPENVPN_PID" || true
wait "$OPENVPN_PID" 2>/dev/null || true
fi
}
trap clean_up EXIT
# If no config present, just drop to the requested shell/command
if [ ! -f "$OVPN_CONF" ]; then
echo "No OpenVPN config found at $OVPN_CONF."
echo "Mount your .ovpn at /etc/openvpn/client.ovpn or set OVPN_CONF."
echo "Starting the container WITHOUT a VPN."
exec "$@"
fi
# If a creds file exists, tighten perms
if [ -f "$OVPN_CREDS" ]; then
chmod 600 "$OVPN_CREDS" || true
fi
if [ "$AUTO_START" = "1" ]; then
echo "Starting OpenVPN using: $OVPN_CONF"
# start OpenVPN in background so we can provide a shell in the same network namespace
openvpn --config "$OVPN_CONF" --auth-nocache --verb 3 &
OPENVPN_PID=$!
# wait for tun interface to appear (timeout 20s)
i=0
until ip addr show dev tun0 >/dev/null 2>&1 || [ $i -ge 40 ]; do
sleep 0.5
i=$((i+1))
done
if ip addr show dev tun0 >/dev/null 2>&1; then
echo "tun0 is up."
else
echo "Warning: tun0 did not appear within timeout. OpenVPN may have failed."
echo "Check logs with: docker logs <container> (or run without -d to see logs)."
fi
fi
# Exec the requested command (default: bash). Network namespace is shared with the running openvpn process.
exec "$@"
EOF
RUN chmod +x /usr/local/bin/entrypoint.sh
# Default to interactive bash so `docker run -it` gives you a shell
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["bash"]
@timching
Copy link
Author

timching commented Dec 5, 2025

just a vibe coding script helps me
also saw this similar redit post https://www.reddit.com/r/docker/comments/1dritn0/can_devnettun_be_used_by_multiple_docker/

execute examples

docker run --rm -it --cap-add=NET_ADMIN --device /dev/net/tun -e AUTO_START=1 tailscale-shell
docker run --rm -it --cap-add=NET_ADMIN --device /dev/net/tun -v $(pwd)/vpn:/etc/openvpn:ro -e AUTO_START=1 vpn-shell

and in mac seems sometimes sucks & buggy dunno why (maybe used same /dev/net/tun? read more, see diagrams)
https://www.reddit.com/r/docker/comments/n3a0eh/kernel_modules_and_docker_for_mac/

but not yet well tested in mac & ubuntu

@timching
Copy link
Author

timching commented Dec 5, 2025

bwt you can use docker compose with tailscale's official image https://tailscale.com/kb/1282/docker#code-examples
it's just my use case prefer docker image only :p

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment