Instalar o Pterodactyl Panel para gameservers

Publicado a 17 min de leitura

O Panel e o Wings são dois programas distintos, com duas tarefas distintas. Quem percebeu isso instala o Pterodactyl em meia hora. Quem não percebeu passa dias à procura do erro.

O Pterodactyl é a interface livre mais utilizada para gameservers. A fama de ser complicado quase nunca vem da instalação em si, mas de um único mal-entendido: o Pterodactyl não é um programa, são dois. Este guia separa os dois com clareza, mostra as diferenças entre Debian e Ubuntu e percorre depois as mensagens de erro que levam qualquer pessoa a copiá-las tal e qual para o motor de busca.

Panel e Wings: dois programas, dois papéis

O Panel é uma aplicação PHP assente em Laravel. Fornece a interface web, gere utilizadores, permissões, bases de dados e agendamentos, e guarda tudo numa base de dados MySQL ou MariaDB própria. O Panel nunca arranca um gameserver por si. Nem sequer conhece o Docker.

O Wings é um único programa escrito em Go. Corre em cada máquina onde os gameservers devem efetivamente correr, fala com o daemon do Docker, arranca containers, transmite a consola e disponibiliza o acesso SFTP. O Wings não tem interface web nem base de dados. Escuta numa porta HTTP e espera instruções do Panel.

Os dois comunicam exclusivamente através de HTTP, nos dois sentidos, com tokens assinados. Daí resultam três consequências que convém aceitar desde o início:

  • O Panel tem de alcançar o node através de um nome de domínio, e não de um IP. O certificado está preso ao nome.
  • O Panel e o Wings têm de falar o mesmo protocolo. Panel em HTTPS com Wings em HTTP não funciona, porque o browser bloqueia a ligação da consola.
  • Os dois relógios têm de estar certos. Os tokens têm um tempo de vida de poucos minutos.

Quem interiorizar estas três frases já evitou metade dos problemas típicos do Pterodactyl.

Requisitos e a escolha do sistema

O Pterodactyl 1.11 e mais recente exige PHP 8.2 ou 8.3. É neste ponto que a maioria dos guias fica imprecisa, porque as distribuições fornecem versões muito diferentes. Em julho de 2026, a situação nas fontes padrão é a seguinte:

SistemaPHPBase de dadosnginx
Debian 128.2 (serve)MariaDB 10.111.22
Debian 138.4 (demasiado recente)MariaDB 11.81.26
Ubuntu 24.048.3 (serve)MySQL 8.0 ou MariaDB 10.111.24
Ubuntu 22.048.1 (demasiado antiga)MySQL 8.0 ou MariaDB 10.61.18
Debian 117.4 (demasiado antiga)MariaDB 10.51.18

Este guia é feito à medida de Debian e Ubuntu. Em AlmaLinux, Rocky Linux e Oracle Linux não existe apt, pelo que nenhum dos comandos de pacotes seguintes se aplica.

Consequência prática: o Debian 12 e o Ubuntu 24.04 são os dois sistemas em que o Panel corre sem fontes externas. No Ubuntu 22.04 precisa do PPA do Ondřej Surý, no Debian 13 do respetivo equivalente para Debian, porque ali é o PHP 8.4 que fornece o metapacote e a composer.json do Panel exige expressamente ^8.2 || ^8.3. Na prática, a execução do Composer até passa com PHP 8.4, mas isso é terreno por confirmar e não é um estado em que se queira operar um servidor produtivo. Quem quiser poupar-se à fonte adicional escolhe Debian 12 ou Ubuntu 24.04.

Já os dois sistemas demasiado antigos não falham no comando de pacotes, mas apenas dois passos mais à frente, e é isso que os torna traiçoeiros. O apt install php corre no Ubuntu 22.04 (PHP 8.1.2) e no Debian 11 (PHP 7.4.33) com código de saída 0, e só depois a execução do Composer aborta, no Ubuntu 22.04 com brick/math requires php (^8.2) failed e no Debian 11 com aws/aws-sdk-php requires php (>=8.1) failed. Se tiver mesmo de ficar num destes sistemas, instale a partir da fonte externa pacotes com versão explícita (php8.3, php8.3-cli, php8.3-fpm e assim por diante) em vez do metapacote php, caso contrário volta a entrar a versão da distribuição.

Segunda diferença que surpreende com regularidade: a Debian não fornece qualquer pacote mysql-server. Ali o MariaDB é a escolha assente, e isso é perfeitamente aceitável, porque o Pterodactyl exige MariaDB 10.2 ou mais recente. Quem escrever apt install mysql-server em Debian recebe E: Unable to locate package mysql-server e passa a procurar no sítio errado.

Para o node com o Wings, estas regras de PHP não se aplicam de todo. O Wings é um programa em Go com ligação estática e precisa apenas de Docker e de um kernel minimamente atual. O node pode perfeitamente ser Debian 13 enquanto o Panel corre em Debian 12.

Instalar o Panel

Todos os comandos seguintes são executados como root. Primeiro os pacotes de base. Tenha o cuidado de incluir as extensões de PHP na totalidade, porque um php-bcmath em falta só dá nas vistas na execução do Composer.

apt update
apt -y install curl ca-certificates gnupg lsb-release tar unzip git
apt -y install mariadb-server nginx redis-server
apt -y install php php-cli php-common php-gd php-mysql php-mbstring php-bcmath php-xml php-fpm php-curl php-zip

Verifique de imediato se a versão serve, antes de avançar:

php -v
php -m | grep -E "bcmath|mbstring|curl|zip|gd|xml"

A seguir, o Composer e os ficheiros do Panel:

curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
mkdir -p /var/www/pterodactyl
curl -Lo /var/www/pterodactyl/panel.tar.gz https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz
tar -xzf /var/www/pterodactyl/panel.tar.gz -C /var/www/pterodactyl

A base de dados. Crie o utilizador em 127.0.0.1 e não em localhost, caso contrário entra o acesso por socket e o Laravel acaba por receber uma recusa de acesso apesar de a palavra-passe estar correta. Como destrinçar esse caso está explicado no nosso artigo sobre Access denied for user.

mariadb -u root -e "CREATE DATABASE panel;"
mariadb -u root -e "CREATE USER 'pterodactyl'@'127.0.0.1' IDENTIFIED BY 'AquiUmaPalavraPasseLonga';"
mariadb -u root -e "GRANT ALL PRIVILEGES ON panel.* TO 'pterodactyl'@'127.0.0.1' WITH GRANT OPTION;"
mariadb -u root -e "FLUSH PRIVILEGES;"

A base de dados deve ser protegida logo a seguir, e para isso temos um artigo próprio sobre como proteger o MariaDB e o MySQL.

Agora a configuração propriamente dita. Os comandos p:environment são interativos e perguntam pelo URL do Panel, fuso horário, driver de cache e acesso à base de dados:

cd /var/www/pterodactyl
cp .env.example .env
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
php artisan key:generate --force
php artisan p:environment:setup
php artisan p:environment:database
php artisan migrate --seed --force
php artisan p:user:make
chown -R www-data:www-data /var/www/pterodactyl/*

Em p:environment:setup, escolha Redis como driver de sessão e de cache, para que a fila descrita mais abaixo funcione como deve ser. E introduza o URL do Panel com https://. Um http:// neste ponto gera mais tarde conteúdos mistos e uma consola que fica eternamente à espera de ligação.

O nginx e a armadilha das versões

As bases da configuração do servidor web estão no nosso artigo sobre como instalar o nginx. Para o Pterodactyl há dois pormenores importantes, e é neles que os modelos prontos que circulam pela internet falham com regularidade.

Primeiro, o socket do PHP-FPM. O nome do ficheiro contém a versão de PHP e difere consoante o sistema. Verifique em vez de adivinhar:

systemctl status php8.2-fpm
ls /run/php/

No Debian 12 o socket chama-se php8.2-fpm.sock, no Ubuntu 24.04 php8.3-fpm.sock. Um caminho errado aqui produz exatamente a página 502 Bad Gateway que tanta gente procura. A consulta do estado vem propositadamente antes: o ficheiro do socket só nasce quando o serviço FPM está a correr. Se ele não estiver arrancado, /run/php/ está vazio e conclui-se erradamente que o caminho está errado. Se o serviço ainda não estiver a correr, resolve-se com systemctl enable --now php8.2-fpm, com o número de versão que corresponder à sua instalação.

Segundo, a forma de escrever o HTTP/2. A diretiva nova http2 on; só existe a partir do nginx 1.25.1. Em Debian 12 (1.22), Ubuntu 22.04 (1.18) e também em Ubuntu 24.04 (1.24) tem de usar a forma antiga listen 443 ssl http2;, caso contrário o arranque aborta com nginx: [emerg] unknown directive "http2". Só o Debian 13, com o nginx 1.26, entende as duas formas.

server {
    listen 443 ssl http2;
    server_name panel.example.com;
    root /var/www/pterodactyl/public;
    index index.php;
    client_max_body_size 100m;

    ssl_certificate /etc/letsencrypt/live/panel.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/panel.example.com/privkey.pem;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTP_PROXY "";
    }
}

Fila de tarefas e agendamento

Sem estas duas peças, o Panel parece funcional, mas não envia mensagens de correio nem executa tarefas agendadas. A estrutura do ficheiro de unit está explicada em detalhe no nosso artigo sobre como criar um serviço systemd; aqui fica a versão final:

[Unit]
Description=Pterodactyl Queue Worker
After=redis-server.service

[Service]
User=www-data
Group=www-data
Restart=always
RestartSec=5s
ExecStart=/usr/bin/php /var/www/pterodactyl/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now redis-server
systemctl enable --now pteroq.service

A isto junta-se uma entrada na crontab do root; os pormenores da sintaxe encontram-se no artigo Configurar um cronjob em Linux:

* * * * * php /var/www/pterodactyl/artisan schedule:run >> /dev/null 2>&1

Instalar o Wings no node

A partir daqui trabalha na máquina que vai executar os gameservers. Pode ser o mesmo servidor, mas não tem de ser. O requisito é o Docker, cuja instalação descrevemos no artigo Instalar o Docker em Debian e Ubuntu.

mkdir -p /etc/pterodactyl
curl -L -o /usr/local/bin/wings https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_amd64
chmod u+x /usr/local/bin/wings
wings version

Em servidores ARM o nome do ficheiro é wings_linux_arm64. Se descarregar o pacote errado, a shell limita-se a responder cannot execute binary file: Exec format error. Não estranhe o resultado da última linha: o wings version responde com um v duplicado, ou seja, algo como wings vv1.13.1. Vem assim de origem e não é sinal de uma instalação estragada.

O ficheiro /etc/pterodactyl/config.yml não é escrito por si. É criado automaticamente daqui a pouco. Para já, crie apenas o serviço:

[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

Ainda não arranque nada. Sem configuração, o Wings aborta de imediato, com uma mensagem que aponta, no essencial, para um ficheiro de configuração em falta em /etc/pterodactyl/config.yml. Neste ponto isso é perfeitamente normal e não é um erro.

Uma palavra sobre o swap: muitos guias antigos exigem swapaccount=1 em /etc/default/grub. Isso diz respeito exclusivamente a sistemas com cgroup v1. O Debian 12 e 13, bem como o Ubuntu 22.04 e 24.04, usam cgroup v2 por predefinição, e aí a entrada é supérflua. Pode verificá-lo com docker info. Se aí aparecer WARNING: No swap limit support, o limite de memória não abrange a área de swap. Como dimensionar o swap de forma sensata está no artigo Configurar swap.

Certificado para o node, não apenas para o Panel

O erro de raciocínio mais frequente: arranja-se um certificado para panel.example.com e depois estranha-se que o node não funcione. O Wings precisa de um certificado próprio para o seu próprio nome de domínio, por exemplo node1.example.com. Os dois nomes podem apontar para o mesmo IP, mas são dois nomes.

Num node puro não corre nenhum servidor web, e por isso o modo autónomo do Certbot é o caminho mais simples. Para isso, a porta 80 tem de estar acessível a partir do exterior durante esse curto período:

apt -y install certbot
certbot certonly --standalone -d node1.example.com

Quem gere muitos nodes fica mais bem servido com um certificado wildcard, que descrevemos no artigo Certificado wildcard da Let's Encrypt.

Dois obstáculos que custam muito tempo:

  • O Wings lê os ficheiros do certificado no arranque. Depois de uma renovação, o serviço tem de ser recarregado. Coloque em /etc/letsencrypt/renewal-hooks/deploy/ um pequeno script com systemctl restart wings. Sem isso, o node corre 90 dias de forma impecável e falha depois aparentemente sem motivo.
  • Desligue o proxy da Cloudflare para o nome do node. A nuvem laranja abre a ligação TLS e substitui o certificado. O Panel passa então a receber um certificado que não corresponde ao emissor esperado, e a ligação da consola por WebSocket comporta-se de forma imprevisível. O registo A do node pertence ao cinzento.

Criar e ligar o node

No Panel, em Admin, Locations, crie primeiro uma localização e, a seguir, o node em Nodes. Os campos que realmente contam:

  • FQDN: node1.example.com, exatamente o nome que consta do certificado.
  • Communicate over SSL: ativado, se o Panel correr sobre HTTPS. Caso contrário, mais vale nem começar.
  • Behind Proxy: ativar apenas se existir mesmo um reverse proxy à frente do Wings e for esse a terminar o TLS.
  • Daemon Port: 8080. Daemon SFTP Port: 2022.
  • Memory e Disk: os limites que o Panel respeita ao distribuir servidores.

Depois de guardar, abra o separador Configuration do node. Aí o Panel gera um comando com um token de utilização única. Execute-o no node:

cd /etc/pterodactyl
wings configure --panel-url https://panel.example.com --token TOKEN --node 1

Com isso, o Wings vai buscar a sua configuração completa e escreve /etc/pterodactyl/config.yml. Verifique o conteúdo: se no endereço do Panel estiver http:// em vez de https://, copiou o comando de um ambiente em que o URL do Panel está mal registado. Corrija-o na origem com php artisan p:environment:setup, e não à mão no ficheiro YAML.

A seguir, no separador Allocations, introduza o IP do node e os intervalos de portas pretendidos, por exemplo 25565 a 25600 para Minecraft. Sem pelo menos uma atribuição livre não é possível criar nenhum servidor.

Só agora deve arrancar o serviço:

systemctl enable --now wings
systemctl status wings

As portas têm de estar abertas na firewall. Com o ufw, cujas bases abordamos no artigo Configurar a firewall ufw:

ufw allow 8080/tcp
ufw allow 2022/tcp
ufw allow 25565:25600/tcp
ufw allow 25565:25600/udp

Quando o Wings não se liga

O Panel mostra um símbolo vermelho no node ou devolve um erro ao criar um servidor. Trate as mensagens pela ordem em que surgem, porque são surpreendentemente claras.

cURL error 7: Failed to connect ... Connection refused

O servidor do Panel não alcança a porta. Ou o Wings não está a correr, ou a firewall bloqueia, ou o serviço está à escuta no endereço errado. Verifique por esta ordem:

systemctl status wings
ss -tlnp | grep 8080
journalctl -u wings -n 50 --no-pager

E a partir do servidor do Panel, que é o teste decisivo:

curl -v https://node1.example.com:8080

Uma resposta HTTP, mesmo um 404 com conteúdo JSON, é neste ponto um sucesso. Prova que DNS, firewall, porta e TLS funcionam em conjunto.

cURL error 60: SSL certificate problem

O certificado do node não está a ser aceite. Com self signed certificate está a usar um certificado criado por si, e o Panel não lida com isso porque a biblioteca subjacente não conhece exceções. Com unable to get local issuer certificate falta normalmente a cadeia intermédia, ou seja, a sua configuração do Wings aponta para cert.pem em vez de fullchain.pem. Com certificate has expired a renovação até correu, mas o Wings ainda tem o ficheiro antigo em memória, veja o ponto do reinício mais acima.

cURL error 28: Operation timed out

Nenhuma resposta e nenhum reset. Isto cheira a uma firewall que descarta pacotes em vez de os rejeitar, ou a um node atrás de NAT. Um caso particular: Panel e Wings no mesmo servidor, com o Panel a contactar o próprio IP público. Algumas redes não devolvem esse trajeto. A solução passa por uma entrada em /etc/hosts no servidor do Panel, que aponte o nome do node para o endereço interno.

Cannot connect to the Docker daemon at unix:///var/run/docker.sock

O Wings está a correr, o Docker não. O systemctl status docker esclarece isso numa linha. Ainda assim, o node aparece muitas vezes como acessível no Panel, porque a consulta de estado funciona, mas qualquer arranque de servidor falha.

O node responde, mas todas as ações são recusadas

Há um token errado. Isso acontece depois de um novo wings configure com um token antigo, ou depois de o node ter sido apagado e criado de novo no Panel. Solução: gerar um token novo no separador Configuration do Panel, executar outra vez o comando e reiniciar o Wings. Não vale a pena andar a adivinhar no ficheiro YAML.

Para todos os casos em que a mensagem continua pouco clara, pare o serviço e arranque o Wings em primeiro plano. A saída é bastante mais conversadora do que o journal:

systemctl stop wings
wings --debug

Também ajuda o modo de relatório integrado, que recolhe a configuração, o estado do Docker e os dados do sistema:

wings diagnostics

Desfasamento horário entre o Panel e o node

Este erro é traiçoeiro porque se parece com um problema de rede. Sintomas: o node é apresentado no Panel como acessível, os servidores podem ser criados, mas a consola fica presa a estabelecer ligação e o acesso SFTP recusa credenciais corretas.

A razão está na própria arquitetura. O Panel assina tokens de vida curta, cuja validade se conta em minutos. O Wings verifica o momento de emissão e o prazo de validade contra o seu próprio relógio. Se os dois sistemas divergirem mais do que alguns minutos, o Wings descarta todos os tokens por expirados ou por ainda não válidos, mesmo que tenham acabado de ser gerados.

Importante para perceber: não se trata do fuso horário. O Panel e o node podem ter fusos horários diferentes configurados, isso é irrelevante. Trata-se do instante absoluto. Verifique nas duas máquinas:

date -u
timedatectl status

Na saída do timedatectl tem de constar System clock synchronized: yes e NTP service: active. Caso contrário:

timedatectl set-ntp true

Em sistemas sem systemd-timesyncd, por exemplo depois de uma instalação mínima, instale o chrony e verifique a sincronização:

apt -y install chrony
chronyc tracking

O valor em System time deve situar-se na ordem dos milissegundos. As máquinas virtuais clonadas a partir de uma imagem ou repostas a partir de um snapshot são a origem mais frequente de desvios maiores.

Como perceber que está mesmo a funcionar

Um símbolo verde no Panel é apenas a primeira de cinco provas. Percorra a lista e passa a ter a certeza:

  1. O systemctl is-active wings devolve active, e um journalctl -u wings -n 20 não mostra erros recorrentes.
  2. O node, na vista geral, comunica a capacidade real de memória e de armazenamento do servidor de destino, e não apenas os limites registados no Panel. Estes números chegam do node em direto e são uma prova de que a comunicação funciona.
  3. Crie um servidor de teste. Na vista geral, ele passa pelo estado Installing e depois é apresentado normalmente. No node, o docker ps -a mostra o container correspondente.
  4. Arranque o servidor e observe no browser saída de consola em curso. É essa a prova da ligação WebSocket e, com ela, do certificado e da hora ao mesmo tempo.
  5. Ligue-se por SFTP na porta 2022 com as suas credenciais do Panel e veja os ficheiros do servidor. Fica assim confirmada também a segunda porta do Wings.

A instalação só está completa quando os cinco pontos batem certo. Os pontos quatro e cinco são, por experiência, os que falham com mais frequência, apesar de até aí o Panel parecer perfeitamente normal.

Operar o Panel e o Wings juntos ou separados

As duas coisas são possíveis. Num servidor único basta ter dois cuidados: use dois nomes de domínio diferentes no mesmo IP, um para o Panel na porta 443 e outro para o node na porta 8080. E conte com o facto de a fome de memória dos gameservers travar também o Panel quando as coisas apertarem.

A partir do segundo node, separar as duas partes é de qualquer forma o caso normal e tem um efeito secundário agradável: um gameserver sobrecarregado ou sob ataque não arrasta consigo a interface de administração. Para a proteção de base das duas máquinas vale a pena consultar a nossa checklist para servidores root novos, bem como os artigos sobre proteção do SSH e fail2ban.

Para terminar, uma nota prática para a operação: as imagens Docker dos gameservers trazem consigo o seu próprio ambiente Java. Não precisa de instalar Java no node. Quem, ainda assim, quiser testar alguma coisa fora do Pterodactyl encontra os caminhos adequados nos nossos artigos sobre Java 21 em Debian e sobre o servidor de Minecraft em Debian. E mantenha o espaço em disco debaixo de olho, porque as imagens e os backups crescem depressa; a esse tema assenta bem o artigo Disco cheio em Linux.

Perguntas frequentes

Qual é a diferença entre o Pterodactyl Panel e o Wings?
O Panel é a interface web em PHP, com base de dados, que gere utilizadores, permissões e configuração. O Wings é um programa em Go separado, que corre no node, fala com o Docker e arranca efetivamente os gameservers. O Panel nunca executa um gameserver por si, e o Wings não tem interface web. Os dois comunicam entre si através de HTTP.
De que versão de PHP preciso para o Pterodactyl?
O Pterodactyl 1.11 e mais recente exige PHP 8.2 ou 8.3. O Debian 12 fornece a 8.2 e o Ubuntu 24.04 fornece a 8.3, e nesses sistemas funciona sem fontes externas. O Ubuntu 22.04 fornece apenas a 8.1 e o Debian 13 já a 8.4, e em ambos os casos precisa de um repositório de PHP adicional.
Porque é que o Wings não se liga ao Panel?
Verifique por esta ordem: o serviço está a correr (systemctl status wings), a porta 8080 está aberta (ss -tlnp), o servidor do Panel alcança o node (curl -v https://node1.example.com:8080), o certificado corresponde ao FQDN e os dois relógios estão sincronizados. Para mais pormenores, arranque o Wings em primeiro plano com wings --debug.
Que portas tenho de abrir para o Pterodactyl?
No servidor do Panel, a porta 443 para HTTPS e a porta 80 para a renovação do certificado. No node, a porta 8080 para a comunicação do Wings, a porta 2022 para SFTP e ainda os intervalos de portas dos próprios gameservers, que no Minecraft começam habitualmente na 25565.
Preciso de um certificado SSL próprio para cada node?
Sim. O Panel contacta o node através do nome de domínio dele, e o certificado do Panel não chega para isso. Ou obtém um certificado próprio por node, ou usa um certificado wildcard para todo o subdomínio. Depois de cada renovação, o Wings tem de ser reiniciado.
Porque é que a consola do servidor fica presa no browser a estabelecer ligação?
Na maioria dos casos é a hora. O Panel assina tokens com um tempo de vida muito curto e o Wings verifica-os contra o seu próprio relógio. Se as horas de sistema divergirem mais do que alguns minutos, todos os tokens são descartados. Verifique nas duas máquinas com date -u e timedatectl status. A segunda causa frequente é um Panel em HTTPS com um node sem SSL.
Tenho de instalar Java no node?
Não. As imagens Docker para Minecraft e para outros jogos em Java trazem consigo o seu próprio ambiente de execução. No node basta o Docker. Uma instalação de Java no anfitrião não é utilizada pelo Pterodactyl.

Pterodactyl Servidor de jogos Wings Docker Panel Minecraft Linux Debian Ubuntu