pterodactyl/panel
Pterodactyl is a free, open-source game server management panel built with Laravel. It isolates servers in Docker containers and provides a secure admin UI, user management, APIs, and scalable control for communities and hosting providers.
Pterodactyl is a Laravel application (not a typical drop-in package) that manages game servers through a companion daemon, Wings. You leverage it by extending the panel, automating it through its two REST APIs, or authoring "eggs" that define how servers are provisioned.
Minimal setup:
composer install, copy .env.example to .env, php artisan key:generate, then php artisan p:environment:setup and p:environment:mail.php artisan migrate --seed (the seeders populate nests and core eggs), then php artisan p:user:make to create your first admin.php artisan queue:work) and the scheduler — server creation, installs, and deletions are all queued jobs, so nothing works without them.sudo apt update && sudo apt install --only-upgrade 'php8.*' — rather than bumping a single extension.Where to look first:
app/Services/ — the real business logic (server creation, environment, installations). Controllers are thin; extend here.app/Models/ and app/Contracts/Repository/ — Eloquent models with a repository layer behind them.app/Http/Controllers/Api/Client and .../Api/Application — the two API surfaces you'll integrate against.database/Seeders/ — canonical examples of nest/egg structure.resources/scripts/ — the React frontend, if you need to touch the UI.First use case: generate an Application API key, then script a full onboarding flow — create user → create server → poll until install completes. This is the highest-leverage thing most developers do with the panel.
Egg authoring and distribution. Eggs are JSON documents describing Docker images, startup command, config files to parse, variables, and an install script. In this beta, exporting an egg from the admin area downloads a ready-to-reimport .json file, so a practical workflow is: build a server manually, tune it, export the egg, commit it to your repo, and import it into staging/production via the admin UI or the Application API. Treat exported eggs as versioned artifacts.
API-first automation. Use the Application API (/api/application) for admin operations (users, nodes, servers, nests, eggs) and the Client API (/api/client) for per-server actions (power, files, backups, schedules, websockets). Store keys per-environment; the Application API key is effectively root for the panel.
Service-layer extension. Rather than editing controllers, wrap or extend the services in app/Services/. For example, hook ServerCreationService or listen to the model/job events it fires to enforce naming conventions, auto-assign allocations, or sync to an external billing system.
Event-driven integrations. The panel dispatches Eloquent and domain events for server lifecycle, user creation, and backups. Register listeners in a small service provider and push to your own queue — do not do synchronous HTTP inside the panel's request cycle.
Frontend customization. Themes/skins live in resources/scripts (React) and Blade views in resources/views. Keep custom Blade in your own namespace and prefer overriding published views over editing vendor assets.
Deployment. Pin the Docker tag (ghcr.io/pterodactyl/panel:v2.0.0-beta.2 for the beta line, or the matching v1.15.2 tag if you are still on 1.x) rather than tracking latest, and keep Wings and panel versions aligned — mismatched panel/daemon pairs are a common source of confusing failures.
Custom images must mirror the hardened PHP config. The official image now ships PHP 8.3.36 and disables session.upload_progress in php-fpm as part of the php2shell mitigation. If you build your own panel image (common for agencies with a baked-in theme), replicate that ini change and pin the PHP base image, otherwise you silently reintroduce the vulnerability while believing you patched it.
composer update.composer/image pulls over cherry-picking, since beta upgrades should stay mechanical.php8.* packages in one transaction; a partial upgrade can leave mismatched extensions that fail confusingly at boot.session.upload_progress is disabled on purpose. If you have bespoke upload flows relying on $_SESSION upload progress keys, they will stop working on the patched image. Use the panel's own file-upload endpoints or client-side progress instead of re-enabling the directive.Queue\Jobs entry. Check php artisan queue:failed before touching the database. Note that a PHP upgrade implies restarting workers — php artisan queue:restart — or they keep running the old, unpatched interpreter..env (especially APP_URL, SESSION_DRIVER, or mail settings) run php artisan optimize:clear. A cached config is the cause of most "my changes did nothing" reports.Gate/Policy covers API authorization for client routes.X-RateLimit-* headers if you sync on a schedule.vendor/. Register extension points in your own service provider so beta upgrades remain composer/image pulls rather than manual patch reapplications.APP_DEBUG=true plus storage/logs/laravel.log covers the panel; daemon-side problems surface in Wings logs on the node, so check both before assuming a panel bug. After a security patch, verify the running PHP version (php -v and php-fpm -v) rather than trusting the package manager's report.How can I help you explore Laravel packages today?