Self Hostd with Docker Compose

Run the Self Hostd launcher as a sidecar next to itzg/minecraft-server: one compose file, a pairing code entered once, and no published ports.

The short version

Run the Self Hostd launcher as a sidecar container next to itzg/minecraft-server in one compose file. The launcher reaches the Minecraft container over the compose network, so you publish no ports at all. Enter the pairing code once and the stored token keeps it linked across restarts.

If your server runs in Docker, the launcher slots in as a second service in the same compose file. It carries players in through the Hostd tunnel and keeps your server on the panel, and because the launcher only makes outbound connections, the compose file needs no ports: section at all: nothing on your machine is exposed to the internet.

#The compose file

services:
  mc:
    image: itzg/minecraft-server
    environment:
      EULA: "TRUE"
      TYPE: PAPER
      MEMORY: 4G
    volumes:
      - ./data:/data
    restart: unless-stopped

  selfhostd:
    image: hostdservers/selfhostd
    environment:
      SELFHOSTD_PAIR_CODE: "123456"
      SELFHOSTD_LOCAL_HOST: mc
      SELFHOSTD_LOCAL_PORT: "25565"
    volumes:
      - ./selfhostd:/data
    restart: unless-stopped

Replace 123456 with the pairing code from your panel, then docker compose up -d. That is the only edit the file ever needs: pairing saves the token into ./selfhostd/selfhostd.properties, and on every later start the leftover pairing code is ignored, so restarts, host reboots and docker compose up after edits all reconnect on their own.

SELFHOSTD_LOCAL_HOST: mc points the tunnel at the game container by its service name over the default compose network, which keeps working when either container restarts. The image is published for x86 and ARM, so the same file works on a NAS, a Raspberry Pi and an ordinary PC.

#Keeping it up to date

docker compose pull selfhostd && docker compose up -d selfhostd

The launcher lives in the image, so pulling is the whole update. Your token is in the ./selfhostd volume and survives it.

#After buying a custom address

A custom address starts working the next time the launcher connects, so run docker compose restart selfhostd once after buying one. Both addresses work from then on.

#What the panel shows in this setup

The launcher is not managing the server process here, so the panel shows your server's status and address, but the console stays with the game container: use docker compose logs -f mc or the itzg image's rcon-cli. If you want panel console and backups, run the launcher directly with a start command instead, as in the command-line guide.

#If it does not connect

docker compose logs selfhostd tells the story. "That code is wrong or has expired" means the pairing code lapsed before first use: generate a fresh one in the panel, put it in SELFHOSTD_PAIR_CODE, and docker compose up -d again. Once paired you will see panel link up and tunnel up: players can join at ... in the logs.

#Cleaning up

docker compose down and delete the folder, then docker image rm hostdservers/selfhostd if you want the image gone too. Nothing is installed outside Docker. Delete the server in your panel as well to free the address.

#Frequently asked questions

Do I need to publish ports with Self Hostd and Docker?

No. The launcher connects outbound and reaches the Minecraft container over the compose network, so no ports need publishing on the host. That is one fewer thing exposed on your home network.

Where does the token live in a Docker setup?

In a volume mounted into the launcher container, so it survives container recreation. Without that volume you would be re-pairing every time the container is rebuilt.

It won't connect. What should I check first?

Check the launcher can resolve the Minecraft container by its compose service name, and that both containers are on the same network. Name resolution between services is the usual failure in this setup.

Written and maintained by the Hostd engineering team. Last updated 2026-08-07. Notice a mistake? Tell us.