Pterodactyl Docker Compose Installation
A few simple Docker Compose files to easily setup Pterodactyl
December 25, 2023
Pterodactyl’s great! Unfortunately the documentation on how to install Pterodactyl is still lacking.
When I was in middle school (in the United States), I tried to install Pterodactyl and ended up having to restart at least 10 (!) times because of minor mistakes. The only installations that ended up working first try were the installer scripts which often harbored their own issues.
We now live in the age where you can just use Docker and Docker Compose to deploy everything easily. It’s a shame though that the official documentation has no Docker Compose instructions to show for it.
Enough waffling. Here’s how to easily configure it.
Basic assumptions (if you know what you are doing, this guide is still fine):
- You have a domain and it is correctly set up to point
panel.domain.com
andnode.domain.com
to your server - Docker is running on root (unfortunately rootless
cgroupsv2
is broken with non Systemd systems) - Docker is running in an environment that supports
cgroupsv2
to limit container resource usage - Firewall allows: 80/tcp, 443/tcp, 443/udp, 8080/tcp, 8080/udp, and 2022/tcp
Why open 8080/tcp and 8080/udp when we could just use 443? Pterodactyl is rather picky and using 443 exclusively does not play well with Caddy. It is doable though and I encourage you to give it a try if you think you’ve got the chops.
NOTE: If you’ve installed Docker Compose and the docker-compose
command doesn’t work for you, instead
use the docker compose
command in place of all of the uses of docker-compose
in this guide.
In the course of the guide, you should place all the individual components in separate folders. A file structre like the following should suffice:
.
├─ 📁 panel/
│ └ 📄 docker-compose.yml
├─ 📁 wings/
│ └ 📄 docker-compose.yml
├─ 📁 caddy/
│ └ 📁 caddy/
│ └ 📄 Caddyfile
│ └ 📄 docker-compose.yml
...
Let’s start with the easy part (the panel) first (read the instructions!):
version: '3.8'
x-common:
database:
&db-environment
# Do not remove the "&db-password" from the end of the line below, it is important
# for Panel functionality.
MYSQL_PASSWORD: &db-password "CHANGE_ME"
MYSQL_ROOT_PASSWORD: "CHANGE_ME_TOO"
panel:
&panel-environment
# !!!!!
# CHANGE THIS
APP_URL: "https://panel.kunet.dev"
#
# !!!!!
# A list of valid timezones can be found here: http://php.net/manual/en/timezones.php
APP_TIMEZONE: "UTC"
APP_SERVICE_AUTHOR: "noreply@example.com"
# Not strictly necessary unless you need to send mail.
mail:
&mail-environment
MAIL_FROM: "noreply@example.com"
MAIL_DRIVER: "smtp"
MAIL_HOST: "mail"
MAIL_PORT: "1025"
MAIL_USERNAME: ""
MAIL_PASSWORD: ""
MAIL_ENCRYPTION: "true"
#
# ------------------------------------------------------------------------------------------
# DANGER ZONE BELOW
#
# The remainder of this file likely does not need to be changed. Please only make modifications
# below if you understand what you are doing.
#
services:
database:
image: mariadb:10.5
restart: always
networks:
- caddy
command: --default-authentication-plugin=mysql_native_password
volumes:
- "./pterodactyl/database:/var/lib/mysql"
environment:
<<: *db-environment
MYSQL_DATABASE: "panel"
MYSQL_USER: "pterodactyl"
cache:
image: redis:alpine
restart: always
networks:
- caddy
panel:
networks:
- caddy
image: ghcr.io/pterodactyl/panel:latest
restart: always
links:
- database
- cache
volumes:
- "./pterodactyl/var/:/app/var/"
- "./pterodactyl/nginx/:/etc/nginx/http.d/"
- "./pterodactyl/certs/:/etc/letsencrypt/"
- "./pterodactyl/logs/:/app/storage/logs"
environment:
<<: [*panel-environment, *mail-environment]
DB_PASSWORD: *db-password
APP_ENV: "production"
APP_ENVIRONMENT_ONLY: "false"
CACHE_DRIVER: "redis"
SESSION_DRIVER: "redis"
QUEUE_DRIVER: "redis"
REDIS_HOST: "cache"
DB_HOST: "database"
DB_PORT: "3306"
networks:
caddy:
external: true
name: caddy
driver: bridge
Now:
# magic to change the default database password to a secure random value
# (you won't need it for anything else unless you plan to reuse the database in which case you'd want to create a separate database compose file)
sed -i "s/\"CHANGE_ME\"/\"$(tr -dc A-Za-z0-9 < /dev/random | head -c 8)\"/" docker-compose.yml
sed -i "s/\"CHANGE_ME_TOO\"/\"$(tr -dc A-Za-z0-9 < /dev/random | head -c 8)\"/" docker-compose.yml
# create Caddy network
docker network create caddy --driver bridge --subnet 172.14.0.0/16
# now it's time to start it up
docker-compose up -d
# create a user for later
docker exec -it panel_panel_1 php artisan p:user:make
Now, let’s move on to wings
:
version: '3.8'
services:
wings:
image: ghcr.io/pterodactyl/wings:latest
restart: always
networks:
- caddy
ports:
- "2022:2022"
tty: true
environment:
TZ: "UTC"
WINGS_UID: 988
WINGS_GID: 988
WINGS_USERNAME: pterodactyl
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/var/lib/docker/containers/:/var/lib/docker/containers/"
- "/etc/pterodactyl/:/etc/pterodactyl/"
- "/var/lib/pterodactyl/:/var/lib/pterodactyl/"
- "/var/log/pterodactyl/:/var/log/pterodactyl/"
- "/tmp/pterodactyl/:/tmp/pterodactyl/"
- "/etc/ssl/certs:/etc/ssl/certs:ro"
networks:
caddy:
external: true
name: caddy
driver: bridge
Don’t start wings
quite yet… we’ll get back to that soon enough.
Oh, and here’s the Caddy Docker Compose file:
version: '3.8'
services:
caddy:
container_name: caddy
networks:
- caddy
image: caddy:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
- "8080:8080"
- "8080:8080/udp"
volumes:
- ./caddy:/etc/caddy
- ./caddy_data:/data
- ./caddy_config:/config
command: "sh -c 'caddy fmt -w /etc/caddy/Caddyfile && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile'"
networks:
caddy:
external: true
name: caddy
driver: bridge
This is an especially cool Caddy config since it automatically formats the Caddyfile on boot!
.
├─ 📁 panel/
│ └ 📄 docker-compose.yml
├─ 📁 wings/
│ └ 📄 docker-compose.yml
├─ 📁 caddy/
│ └ 📁 caddy/
│ └ 📄 Caddyfile
│ └ 📄 docker-compose.yml
...
And inside the caddy
folder which is in the same folder that the Caddy docker-compose.yml
file is
in is the Caddyfile
(see file structure). Change the domains to the domains to your domains but
keep the http://panel
and http://wings:8080
as they are automatically correctly routed to the other
containers through the magic of Docker’s DNS:
panel.kunet.dev {
reverse_proxy http://panel
}
node.kunet.dev:8080 {
reverse_proxy http://wings:8080
}
These commands on Linux will increase the maximum UDP buffer size, which you might want to do for performance reasons for QUIC (which runs on UDP) and is enabled by default in Caddy:
# needs to be run as root
sysctl -w net.core.rmem_max=2500000
sysctl -w net.core.wmem_max=2500000
Go ahead and start up the Caddy proxy:
Now log into the panel and begin the node creation process in the admin area.
You’ll want to make sure the following options are in order:
- Behind Proxy (we are indeed behind a proxy)
Once it’s been created, head to the configuration tab and copy it all to clipboard.
You’ll want to place the contents of the file into the path /etc/pterodactyl/config.yml
.
After that, you’re free to start up wings
.
When you click on the side bar button for “Nodes” and see a green heart icon next to your new node, you’ll know that the configuration has correctly worked, and you can begin making servers to your heart’s content.
This guide is especially finnicky and difficult, ask questions in the comments or contact me directly.