setupmc.com

Set Up a Velocity Proxy with Docker Compose and itzg/mc-proxy

Run Velocity and Paper together with Docker Compose, route players through the proxy, keep backend ports private, and validate the complete connection path.

Getting Started & Choices
setupmc.com Team

Use the matching tool

Minecraft proxy configurator

Create a Docker Compose setup for Velocity, BungeeCord, or a custom proxy.

Open Minecraft proxy configurator

What this network does

Velocity becomes the only public Minecraft entry point. It authenticates Java players and sends them to one or more backend servers. In this first setup, the network contains:

  • one itzg/mc-proxy container running Velocity
  • one itzg/minecraft-server container running Paper
  • one shared Docker network
  • only TCP port 25565 published by the proxy
  • persistent folders for proxy and server state

If you only need one server and no proxy plugins, stay with the simpler Java server configurator. A proxy adds another security boundary that must be configured deliberately.

Step 1: Generate the proxy Compose baseline

Open the Minecraft Proxy Configurator, select VELOCITY, and keep the public mapping at 25565:25577. Download the generated Compose file.

For the complete network, extend it with a Paper backend:

services:
  proxy:
    image: itzg/mc-proxy:latest
    restart: unless-stopped
    ports:
      - "25565:25577"
    environment:
      TYPE: VELOCITY
      MEMORY: 512m
    volumes:
      - ./proxy:/server
    networks:
      - minecraft
    stdin_open: true
    tty: true

  lobby:
    image: itzg/minecraft-server:java25
    restart: unless-stopped
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      VERSION: "26.1.2"
      ONLINE_MODE: "FALSE"
    volumes:
      - ./lobby:/data
    networks:
      - minecraft

networks:
  minecraft: {}

The backend has no ports entry. Docker Compose makes it reachable as lobby:25565 from the proxy because both services share the minecraft network.

Pin a Minecraft release you have tested instead of copying the example version indefinitely. Check the Java version guide when the selected Paper release changes its runtime requirement.

Step 2: Start once to create configuration files

Create the persistent folders, validate the resolved Compose model, and start both services:

mkdir -p proxy lobby
docker compose config
docker compose up -d
docker compose logs -f proxy lobby

Wait until Paper reports that it is ready and Velocity has created proxy/velocity.toml. Then stop the stack before changing forwarding:

docker compose down

Do not leave the backend in offline mode while its port is public. The next step completes identity forwarding before the network is opened to players.

Step 3: Register the backend in Velocity

Edit proxy/velocity.toml. Keep the proxy listener aligned with the container port and point the backend at the Compose service name:

bind = "0.0.0.0:25577"
online-mode = true
player-info-forwarding-mode = "modern"
forwarding-secret-file = "forwarding.secret"

[servers]
lobby = "lobby:25565"
try = ["lobby"]

localhost would mean the proxy container itself, not the Paper container. Docker service discovery keeps lobby stable even when the backend container receives a new IP address.

Velocity creates proxy/forwarding.secret. The same value must be configured on Paper. Follow the dedicated Velocity modern forwarding guide before starting the public network.

Step 4: Start and validate every layer

Bring the stack back up:

docker compose up -d
docker compose ps
docker compose logs --tail=100 proxy lobby

Confirm these conditions:

  1. Both services remain Up and the proxy becomes healthy.
  2. Velocity logs no forwarding or backend connection error.
  3. docker compose port proxy 25577 shows the public mapping.
  4. docker compose port lobby 25565 returns no published host port.
  5. A Java client can join the host on port 25565 and reaches lobby.

Finally, run server lobby in the Velocity console or use the in-game /server lobby command with appropriate permissions. The backend console should show the forwarded player identity, not an unauthenticated direct login.

Add a second backend later

Add another service, for example survival, to the same network and register it in velocity.toml:

[servers]
lobby = "lobby:25565"
survival = "survival:25565"
try = ["lobby"]

Do not reuse a world directory across backend services. Give every server its own persistent data path and repeat the same forwarding and isolation controls.

Troubleshooting

SymptomLikely causeNext check
Velocity starts, but lobby is unavailableWrong service name, port, or Docker networkFollow the backend connection runbook
Paper says to connect through VelocityModern forwarding differs between proxy and backendCompare mode, enabled flag, and secret
Players can bypass the proxyBackend port was published or allowed by a firewallRemove the backend port mapping and close the rule
Proxy is unhealthy but accepts connectionsHealthcheck port differs from the Velocity listenerAlign SERVER_PORT, bind, and the container port
Existing world is missingA different /data path was mountedStop and verify the bind mount before copying data

Next steps

Frequently asked questions

Short answers to the questions that usually come up while working through this topic.