# How to Deploy MasterDnsVPN Server with Docker on MikroTik RouterOS

> Deploy MasterDnsVPN server with Docker on MikroTik RouterOS v7+. This guide simplifies setting up your custom DNS-tunnel server with minimal configuration.

- Repository: [Amin Mahmoudi/MasterDnsVPN](https://github.com/masterking32/MasterDnsVPN)
- Tags: how-to-guide
- Published: 2026-05-10

---

**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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/server_config.toml) and [`encrypt_key.txt`](https://github.com/masterking32/MasterDnsVPN/blob/main/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:

```text
/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:

```text
/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main//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:

```text
/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:

```text
/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:

```text
/container print

```

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

```text
/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/docker/docker-compose.yml) structure:

```yaml
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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/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`](https://github.com/masterking32/MasterDnsVPN/blob/main/docker/docker-entrypoint.sh) script in the repository, the container checks for [`/data/server_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main//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`](https://github.com/masterking32/MasterDnsVPN/blob/main/encrypt_key.txt) and [`server_config.toml`](https://github.com/masterking32/MasterDnsVPN/blob/main/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.