How to Deploy MasterDnsVPN Server with Docker on MikroTik RouterOS

MasterDnsVPN runs a custom DNS-tunnel server in a multi-architecture Docker container that requires only a persistent /data volume and a DOMAIN environment variable to deploy on MikroTik RouterOS v7+ using the native container subsystem.

MasterDnsVPN is an open-source DNS tunneling solution written in Go by masterking32/MasterDnsVPN. The project distributes a multi-architecture Docker image (ghcr.io/masterking32/masterdnsvpn:latest) that encapsulates the compiled server binary and initialization logic from docker/docker-entrypoint.sh. When deploying on MikroTik RouterOS v7 or newer, you leverage the native /container subsystem to run the image directly on the router hardware, eliminating the need for separate server infrastructure.

Prerequisites for RouterOS Container Deployment

Before starting, ensure your MikroTik device meets these requirements:

  • RouterOS v7+ with the container package enabled (/system package enable container)
  • Writable storage (e.g., /disk1) for persistent configuration data
  • Delegated DNS subdomain (e.g., v.example.com) with NS records pointing to your router
  • Port 53 availability (UDP and TCP) for the DNS tunnel

The container image automatically initializes a default configuration from server_config.toml.simple if no existing configuration is found in the persistent data directory.

Creating the Persistent Storage Mount

MasterDnsVPN stores its server_config.toml and encrypt_key.txt in /data inside the container. You must create a host directory and mount list before creating the container.

Create the host directory and mount configuration:

/file make-dir name=masterdnsvpn-data

/container mounts
add dst=/data list=MasterDnsVPN src=/disk1/masterdnsvpn-data

This mount list named MasterDnsVPN maps the host's /disk1/masterdnsvpn-data to the container's /data path, ensuring your encryption keys and server configuration persist across container restarts and upgrades.

Configuring the Environment Variable

The server requires the DOMAIN environment variable on first startup to determine which DNS zone it will serve. Create an environment list in RouterOS:

/container envs
add key=DOMAIN list=MasterDnsVPN value=v.example.com

Replace v.example.com with the subdomain you delegated to the server. According to the docker/docker-entrypoint.sh script in the repository, this value is injected into the server configuration only during the initial bootstrap when /data/server_config.toml is missing.

Deploying the Container on RouterOS

With the mount list and environment variables configured, create the container definition using the official GitHub Container Registry image:

/container add \
   check-certificate=no \
   dns=1.1.1.1 \
   envlists=MasterDnsVPN \
   hostname=MasterDnsVPN \
   interface=MasterDnsVPN \
   layer-dir="" \
   mountlists=MasterDnsVPN \
   name=MasterDnsVPN \
   remote-image=ghcr.io/masterking32/masterdnsvpn:latest \
   root-dir=/containers/data/MasterDnsVPN \
   start-on-boot=yes

This command references the MasterDnsVPN mount list and environment list created in previous steps. The start-on-boot=yes parameter ensures the DNS tunnel server automatically launches after router reboots.

Forwarding DNS Traffic to the Container

RouterOS requires destination NAT rules to redirect external DNS queries (port 53) to the container's internal interface:

/ip firewall nat add chain=dstnat protocol=tcp dst-port=53 action=dst-nat to-addresses=127.0.0.1 to-ports=53
/ip firewall nat add chain=dstnat protocol=udp dst-port=53 action=dst-nat to-addresses=127.0.0.1 to-ports=53

If the router runs another DNS service (such as the built-in DNS cache), disable or reconfigure it to free port 53. The container binds to UDP/TCP port 53 to accept DNS tunnel traffic as implemented in the server source code.

Verification and Container Management

Verify the container status:

/container print

Look for status=running in the output. Inspect server logs using:

/container log print where name=MasterDnsVPN

The docker/Dockerfile in the repository defines the entrypoint that launches the server binary. If the container fails to start, check that the DOMAIN variable is set correctly and that the /data mount point has sufficient permissions.

Alternative Docker Compose Deployment

For testing on non-RouterOS environments, use the provided docker/docker-compose.yml structure:

services:
  masterdnsvpn:
    image: ghcr.io/masterking32/masterdnsvpn:latest
    restart: unless-stopped
    environment:
      - DOMAIN=v.example.com
    volumes:
      - ./data:/data
    ports:
      - "53:53/tcp"
      - "53:53/udp"

Run with docker compose up -d. This uses the same DOMAIN variable and volume mapping logic as the RouterOS deployment, making migration between platforms straightforward.

Summary

  • MasterDnsVPN runs as a multi-arch Docker container available at ghcr.io/masterking32/masterdnsvpn:latest according to the masterking32/MasterDnsVPN source code
  • RouterOS v7+ requires a mount list for /data persistence and an env-list for the DOMAIN variable as documented in README.MD (lines 90-102)
  • The container initializes server_config.toml from server_config.toml.simple defaults on first run if the persistent volume is empty
  • Port 53 must be forwarded via NAT rules to reach the container's DNS tunnel server
  • Key files referenced: docker/Dockerfile, docker/docker-entrypoint.sh, and server_config.toml.simple

Frequently Asked Questions

How does MasterDnsVPN handle first-time configuration inside Docker?

According to the docker/docker-entrypoint.sh script in the repository, the container checks for /data/server_config.toml on startup. If the file is missing, it copies the template from server_config.toml.simple and injects the DOMAIN environment variable into the configuration before launching the server binary.

Can I run MasterDnsVPN on RouterOS versions older than v7?

No. The /container subsystem required to run OCI-compliant Docker images is only available in RouterOS v7 and newer. Earlier versions lack the container engine necessary to execute the ghcr.io/masterking32/masterdnsvpn:latest image.

What happens if I don't mount a persistent volume for /data?

Without a persistent mount, the container loses its encrypt_key.txt and server_config.toml after reboots or upgrades. This forces the server to regenerate cryptographic keys and reinitialize configuration, breaking existing client connections and requiring reconfiguration of the DNS delegation.

Why does the container need port 53 specifically?

MasterDnsVPN operates as a DNS-tunnel server, intercepting DNS queries to encapsulate VPN traffic. The server binary listens on UDP and TCP port 53 by design, as this is the standard port for DNS traffic. Delegating your subdomain's NS record to the router ensures client DNS queries reach the container.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →