Pterodactyl is an open source game server management panel. It has two parts: the Panel, a PHP web application where you create users and servers, and Wings, a daemon that runs each game server in an isolated Docker container. In this tutorial you will install both on the same Ubuntu 24.04 server, secure the panel with HTTPS, connect Wings as a node and deploy your first game server. Later you can add more machines running Wings to the same panel to grow your game hosting.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS with at least 2 CPU cores and 4 GB of RAM, for example a CubePath VPS or dedicated server. Game servers need their own memory on top of this: a Minecraft server alone typically uses 2 to 4 GB.
- A non-root user with
sudoprivileges. - A domain name,
panel.your_domainin this guide, with a DNS A record pointing to the server's public IP. - These ports open: 80 and 443 (panel), 8080 (Wings API), 2022 (SFTP) and the ports of your game servers, for example 25565 for Minecraft.
Most commands in the panel installation must run as root or with sudo. They are shown with sudo.
Step 1 - Installing the panel dependencies
Ubuntu 24.04 includes PHP 8.3, which the current Pterodactyl panel supports, plus Nginx, MariaDB, Redis and Composer 2. Install everything from the Ubuntu repositories:
sudo apt update
sudo apt install nginx mariadb-server redis-server php8.3-cli php8.3-fpm php8.3-common php8.3-gd php8.3-mysql php8.3-mbstring php8.3-bcmath php8.3-xml php8.3-curl php8.3-zip composer curl tar unzip git
Check that the services are running and that Composer is version 2:
systemctl is-active nginx mariadb redis-server php8.3-fpm
composer --version
active
active
active
active
Composer version 2.7.1 2024-02-09 15:26:28
Open the firewall for SSH and the panel, and enable UFW:
sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
sudo ufw enable
Step 2 - Creating the database
Open the MariaDB shell:
sudo mariadb
Create a database called panel and a user that connects through 127.0.0.1. Replace your_strong_password with a long random password and keep it for Step 4:
CREATE USER 'pterodactyl'@'127.0.0.1' IDENTIFIED BY 'your_strong_password';
CREATE DATABASE panel;
GRANT ALL PRIVILEGES ON panel.* TO 'pterodactyl'@'127.0.0.1' WITH GRANT OPTION;
FLUSH PRIVILEGES;
EXIT;
Test the new account:
mariadb -u pterodactyl -p -h 127.0.0.1 -e "SHOW DATABASES;"
The output must list the panel database.
Step 3 - Downloading the panel
Create the panel directory and download the latest release:
sudo mkdir -p /var/www/pterodactyl
cd /var/www/pterodactyl
sudo curl -Lo panel.tar.gz https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz
sudo tar -xzvf panel.tar.gz
sudo chmod -R 755 storage/* bootstrap/cache/
Create the environment file and install the PHP dependencies. Composer is run as root here, so COMPOSER_ALLOW_SUPERUSER=1 suppresses its warning:
sudo cp .env.example .env
sudo COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
Generate the application encryption key:
sudo php artisan key:generate --force
ImportantThe
APP_KEYvalue in/var/www/pterodactyl/.envencrypts sensitive data in the database. Back it up somewhere safe. If you lose it, the encrypted data cannot be recovered.
Step 4 - Configuring the panel
Pterodactyl provides interactive commands to fill in .env. Run the environment setup first:
sudo php artisan p:environment:setup
Answer the questions as follows:
- Egg Author Email: your email address.
- Application URL:
https://panel.your_domain. - Application Timezone: your time zone, for example
Europe/Madrid. - Cache Driver, Session Driver and Queue Driver:
redis. - Redis Host:
localhost, with no password and the default port6379.
Configure the database connection with the values from Step 2 (host 127.0.0.1, port 3306, database panel, user pterodactyl):
sudo php artisan p:environment:database
Configure how the panel sends email (password resets and notifications). Choose SMTP and enter your mail provider's details, or mail to use the local mail command:
sudo php artisan p:environment:mail
Create the database tables and load the default game templates, called eggs:
sudo php artisan migrate --seed --force
Create the first administrator account. Answer yes to the question about being an administrator:
sudo php artisan p:user:make
Finally, give the web server user ownership of the files:
sudo chown -R www-data:www-data /var/www/pterodactyl/*
Step 5 - Setting up the scheduler and the queue worker
The panel runs periodic tasks through Laravel's scheduler, which needs a cron entry that runs every minute. Open root's crontab:
sudo crontab -e
Add this line:
* * * * * php /var/www/pterodactyl/artisan schedule:run >> /dev/null 2>&1
Jobs such as sending emails and talking to Wings are processed by a queue worker. Create a systemd unit for it:
sudo nano /etc/systemd/system/pteroq.service
[Unit]
Description=Pterodactyl Queue Worker
After=redis-server.service
[Service]
User=www-data
Group=www-data
Restart=always
ExecStart=/usr/bin/php /var/www/pterodactyl/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
StartLimitInterval=180
StartLimitBurst=30
RestartSec=5s
[Install]
WantedBy=multi-user.target
Enable and start the worker:
sudo systemctl enable --now redis-server
sudo systemctl enable --now pteroq.service
systemctl status pteroq.service --no-pager
The status must show active (running). If it does not, check the log with journalctl -u pteroq.service -n 50.
Step 6 - Configuring Nginx with HTTPS
Install Certbot and request a certificate for the panel domain. The certonly mode only obtains the certificate and leaves the Nginx configuration to you:
sudo apt install certbot python3-certbot-nginx
sudo certbot certonly --nginx -d panel.your_domain
Remove the default site and create the panel configuration:
sudo rm /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-available/pterodactyl.conf
Add the following configuration, replacing panel.your_domain in the three places where it appears:
server {
listen 80;
server_name panel.your_domain;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name panel.your_domain;
root /var/www/pterodactyl/public;
index index.php;
access_log /var/log/nginx/pterodactyl.app-access.log;
error_log /var/log/nginx/pterodactyl.app-error.log error;
client_max_body_size 100m;
client_body_timeout 120s;
sendfile off;
ssl_certificate /etc/letsencrypt/live/panel.your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/panel.your_domain/privkey.pem;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1.2 TLSv1.3;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
add_header X-Frame-Options DENY;
add_header Referrer-Policy same-origin;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M";
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_PROXY "";
fastcgi_intercept_errors off;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
}
location ~ /\.ht {
deny all;
}
}
Enable the site, test the syntax and reload Nginx:
sudo ln -s /etc/nginx/sites-available/pterodactyl.conf /etc/nginx/sites-enabled/pterodactyl.conf
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Open https://panel.your_domain in your browser and log in with the administrator account from Step 4.
Step 7 - Installing Docker and Wings
Wings runs every game server inside a Docker container, so Docker Engine must be installed first. Add Docker's official repository and install it:
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
Confirm that Docker is running:
sudo docker run --rm hello-world
The output must include Hello from Docker!.
Download the Wings binary for your CPU architecture into /usr/local/bin:
sudo mkdir -p /etc/pterodactyl
ARCH=$(dpkg --print-architecture)
sudo curl -L -o /usr/local/bin/wings "https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_${ARCH}"
sudo chmod u+x /usr/local/bin/wings
dpkg --print-architecture returns amd64 or arm64, which matches the names of the Wings release files.
Open the ports Wings uses for its API and for SFTP access to game server files:
sudo ufw allow 8080/tcp
sudo ufw allow 2022/tcp
NotePorts published by Docker containers bypass UFW rules, so the game server ports you assign in the next step are reachable even without a UFW rule. Keep this in mind when you decide which ports to allocate.
Step 8 - Adding the node in the panel
In Pterodactyl, a node is a machine running Wings, and every node belongs to a location. In the panel, open the administration area (the gear icon) and:
- Go to Locations, click Create New and create a location, for example
amswith the descriptionAmsterdam. - Go to Nodes, click Create New and fill in:
- FQDN:
panel.your_domain. Wings runs on the same server, so it reuses the panel's domain and certificate. - Communicate Over SSL: Use SSL Connection.
- Total Memory and Total Disk Space: the resources you want to hand out to game servers, leaving room for the panel and the operating system.
- Daemon Port:
8080and Daemon SFTP Port:2022.
- FQDN:
- Save the node, open its Configuration tab and copy the YAML shown there.
Create the Wings configuration file and paste that YAML into it:
sudo nano /etc/pterodactyl/config.yml
Run Wings once in the foreground with debug output to check the configuration:
sudo wings --debug
Wings should start, detect Docker and report that it is listening for requests on port 8080. In the panel, the node now shows a green heart in the Nodes list. Stop Wings with Ctrl+C.
Step 9 - Running Wings as a service
Create a systemd unit so that Wings starts automatically:
sudo nano /etc/systemd/system/wings.service
[Unit]
Description=Pterodactyl Wings Daemon
After=docker.service
Requires=docker.service
PartOf=docker.service
[Service]
User=root
WorkingDirectory=/etc/pterodactyl
LimitNOFILE=4096
PIDFile=/var/run/wings/daemon.pid
ExecStart=/usr/local/bin/wings
Restart=on-failure
StartLimitInterval=180
StartLimitBurst=30
RestartSec=5s
[Install]
WantedBy=multi-user.target
Enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now wings
systemctl status wings --no-pager
The service must be active (running). Logs are available with journalctl -u wings -f.
Step 10 - Deploying your first game server
Before creating a server, the node needs allocations: the IP and port combinations that game servers will listen on.
- In the admin area, open Nodes, select your node and go to the Allocation tab.
- Under Assign New Allocations, enter the server's public IP in IP Address and the ports in Ports, for example
25565-25570. Click Submit. - Go to Servers and click Create New. Choose a name and an owner, select your node and one of the allocations, set the memory and disk limits, and in Nest Configuration select the Minecraft nest and the Paper egg.
- Click Create Server.
Wings pulls the Docker image and runs the egg's install script. When the installation finishes, open the server from the panel's main page, accept the Minecraft EULA if prompted and press Start. The console shows the server starting, and you can connect to your_server_ip:25565 from the game client.
From the server's own page, users can open the console, manage files, connect with SFTP on port 2022 and create schedules.
Troubleshooting
- The panel shows a 500 error: read
/var/www/pterodactyl/storage/logs/and/var/log/nginx/pterodactyl.app-error.log. A wrong database password in.envor files not owned bywww-dataare the most common causes. - The node heart is red: Wings is not reachable. Check
systemctl status wings, that port 8080 is open, and that the FQDN in the node settings matches the certificate in/etc/letsencrypt/live/. - Emails are never sent: the queue worker is not running. Check
systemctl status pteroq. - A game server gets stuck installing: open the server in the admin area and look at the install log, or run
journalctl -u wings -n 100. Missing Docker images or lack of disk space are typical causes.
Conclusion
You now run your own game hosting platform: the Pterodactyl panel manages users and servers over HTTPS, and Wings runs each game server in its own Docker container. To scale out, install Docker and Wings on additional servers and add them as new nodes, import extra eggs for other games from the community, and back up the panel database, the .env file and /var/lib/pterodactyl/volumes, where Wings stores game server data.
