The open-source game-server panel that gives Pelican the player UX, admin tooling, and customisation it deserves.
Screenshots · Why Peregrine? · Quick start · Features · Theme Studio · Plugins · Roadmap
Peregrine is a modern control panel for game servers. It speaks to Pelican (the actively-maintained fork of Pterodactyl) over its API and wraps it in:
- a React 19 player SPA with WebSocket console, full file manager, SFTP, databases, backups, schedules, network and subuser invitations,
- a Filament 5 admin panel for users / servers / plans / eggs / nodes — with one-click Pelican sync,
- a Theme Studio with live split-screen preview to fully rebrand the panel without touching code,
- a Plugin Marketplace backed by a public GitHub registry — install / update / uninstall in one click,
- Docker-first, multi-arch image published to GHCR on every push to
main, - bilingual EN / FR UI, every string translated.
It runs standalone for a single hoster, or wired to a SaaS shop via OAuth2 SSO + Stripe webhook bridge for full subscription-driven provisioning.
Pelican already does the heavy lifting on the daemon side. Peregrine is what sits on top of it, for the people who actually use the panel every day.
| Pelican panel (default) | Peregrine | |
|---|---|---|
| Player UX | Functional, classic | Modern React SPA, dark/light, mobile-friendly |
| File manager | Basic | Full parity (chmod, pull, drag-drop, bulk, archives) |
| Customisation | CSS overrides | Theme Studio with live preview — no code |
| Plugins | None first-party | Marketplace backed by a public GitHub registry |
| Subuser invitations | Manual | First-class plugin, granular permissions, email |
| SSO / Billing bridge | None | OAuth2 + Stripe + Pelican webhook bridge |
| i18n | EN | EN + FR (every string), pluggable |
| Deploy | Multi-step | docker compose up -d → 7-step browser wizard |
If you run a hosting business or want to give your community a panel that doesn't feel like a 2014 admin form, Peregrine is for you.
The heavy lifting happens in your browser. After the container starts, open port 8080 and a 7-step Setup Wizard walks you through language, database, admin account, Pelican credentials, auth mode, optional Bridge, and summary. You never touch
.envmanually.
Two compose files ship in the repo, and that's it:
| File | When to use it |
|---|---|
docker-compose.yml (default) |
All-in-one — bundled MySQL 8.4 + Redis. Turnkey production install. |
docker-compose.external-db.yml |
You already run a managed MySQL / MariaDB / PostgreSQL elsewhere — keep it, get bundled Redis. |
curl -fsSLO https://raw.githubusercontent.com/Knaox/Peregrine/main/docker-compose.yml
docker compose up -d
open http://localhost:8080Works as a Portainer Stack — paste the compose, click Deploy. The Setup Wizard pre-fills the database step with mysql / peregrine / peregrine (override DB_PASSWORD at deploy time).
curl -fsSLO https://raw.githubusercontent.com/Knaox/Peregrine/main/docker-compose.external-db.yml
# Set DB_HOST / DB_DATABASE / DB_USERNAME / DB_PASSWORD via .env or Portainer env vars
docker compose -f docker-compose.external-db.yml up -d
open http://localhost:8080A single image, supervised by supervisord and auto-restarted on crash:
nginx— HTTP server on port 8080php-fpm— PHP 8.3 worker poolphp artisan queue:work— processes Bridge / Stripe / Pelican-mirror webhooks, plugin emails, sync jobs
No separate worker container, no host-level supervisor / systemd setup needed.
git clone https://github.com/Knaox/Peregrine.git && cd Peregrine
composer install --no-dev --optimize-autoloader
pnpm install && pnpm run build
cp .env.example .env && php artisan key:generate && php artisan storage:link
php artisan serve & # HTTP on :8000
php artisan queue:work --daemon & # mail + sync jobsReverse-proxy with nginx / Caddy / Traefik as you normally would.
- Overview — live CPU / RAM / disk / network via Wings WebSocket, uptime, banner image, quick actions gated by permissions.
- Console — xterm.js terminal, persistent command history, Start / Stop / Restart / Kill with granular
control.*gating. - File manager — full Pelican parity: list, read, edit, write, rename, delete, copy, compress, decompress,
chmod(octal), remote URLpull, drag-and-drop upload, folder creation, bulk actions. Read-only mode for users withoutfile.update. - SFTP — credentials panel, clipboard copy, separate SFTP password reset.
- Databases — create, rotate password, delete, view credentials.
- Backups — create, download, lock, restore, delete.
- Schedules — cron presets + advanced editor, run-now, task management.
- Network — allocations list, notes, primary, bulk delete, add.
- Invitations (shipped plugin) — invite users by email with granular permissions, edit pending and active subusers.
- Resources: Users, Servers, Plans, Eggs, Nests, Nodes — one-click Pelican sync.
- Settings — app name, logo, favicon, custom header links, Pelican credentials, auth mode, bridge.
- Email templates — per-locale subject + HTML body, variable placeholders, automatic favicon-as-logo.
- About & Updates — live GitHub release check, Docker-aware update commands with one-click clipboard copy.
- Strict permissions — every Pelican subuser permission key maps to a dedicated policy ability. UI hides what users can't do; API returns 403 if they try anyway.
- Multi-provider auth — local, OAuth2 (SaaSykit-compatible Shop, Paymenter), Google, Discord, LinkedIn — coexist, configurable from
/admin/auth-settings. Native 2FA TOTP with admin enforcement option. - Redis caching — branding, theme, allocations, SFTP credentials, backup/database/schedule lists, settings.
- Queue-safe — plugin Mailables never get serialised into the queue.
- Bilingual EN + FR — every new string lands in both i18n files, same commit.
- Multi-arch image —
linux/amd64+linux/arm64, auto-built on every push tomain.
A full-screen, admin-only React studio at /theme-studio with live split-screen preview — edit on the left, see your panel update in real time on the right. Reachable from Filament → Settings → Appearance → Open Theme Studio.
What you can do without touching a single line of code:
- 7 brand presets (Orange / Amber / Crimson / Emerald / Indigo / Violet / Slate), each with full dark + light variants.
- ~60 design tokens — colors, radii, fonts, shadows, density, layout widths, sidebar styles, border widths, hover scale, glass blur, transition speed, font scale.
- 4 login templates (Centered / Split / Overlay / Minimal) with image upload and 8 background patterns.
- Per-page overrides — fullwidth console, fullwidth file manager, 4-column dashboard.
- Sidebar configurator — widths, blur, floating, classic/rail/mobile, custom nav entries with reordering.
- Footer builder — toggle, free text, list of links.
- Live preview toolbar — switch between 8 scenes (4 user pages, 4 server pages), toggle dark/light, change breakpoint (mobile / tablet / desktop).
- Asset uploads — drag-drop your login background straight into the studio.
- Custom CSS escape hatch — for the 1% the tokens don't cover.
Settings are stored in the settings table (cached in Redis 1 h) and rendered as CSS variables on every page. Reset to factory defaults in one click.
Peregrine has a real plugin system — not theme overrides or hooks. Plugins are mini React + Laravel apps that can register routes, navigation entries, permissions, settings schemas and Filament resources.
Public, GitHub-hosted registry at Knaox/peregrine-plugins. Peregrine fetches the latest registry.json from raw.githubusercontent.com, lists available plugins in Admin → Plugins → Marketplace, and handles install / activate / deactivate / update / uninstall in one click.
- Server Invitations — invite players to your servers by email with granular Pelican permissions, edit pending invites and active subusers, self-protection against locking yourself out, queue-safe email dispatch.
php artisan make:plugin my-pluginScaffolds plugins/my-plugin/ with a service provider, manifest, migrations folder, and a React entry point. See plugins/invitations/ for the reference implementation, and docs/plugins.md for the full plugin developer guide.
Run a private registry by setting MARKETPLACE_REGISTRY_URL in your .env.
Everything is configured through the browser during the 7-step Setup Wizard. The only .env value you set manually before the first boot is APP_URL (it's the absolute base URL for emails, OAuth callbacks and the update checker).
The wizard writes:
| Step | Writes |
|---|---|
| Database (live-tested) | DB_* vars |
| Admin account | First admin user |
| Pelican (live-tested) | PELICAN_URL, PELICAN_ADMIN_API_KEY, PELICAN_CLIENT_API_KEY |
| Auth mode | Local / OAuth2 / Social providers, all configurable post-install too |
| Bridge (optional) | BRIDGE_ENABLED, STRIPE_WEBHOOK_SECRET |
| Summary | Flips PANEL_INSTALLED=true, runs migrations |
Re-run the wizard at any time by setting PANEL_INSTALLED=false. Existing data is preserved.
See .env.example for the full list of recognised variables.
If Peregrine sits behind a reverse proxy (Nginx Proxy Manager, Traefik, Caddy, Cloudflare, …), the proxy must forward the right X-Forwarded-* headers so Laravel knows the request is HTTPS. Without them, Request::isSecure() returns false, signed URLs break and Livewire file uploads fail with a 403.
Open Admin → Settings → Network → Trusted proxies and either:
- Add the IP / CIDR of your reverse proxy (e.g.
192.168.1.10,172.16.0.0/12) - or click "Use Cloudflare IPs" to seed all official Cloudflare ranges, then add your own private proxy IP on top
- or enter
*to trust the connecting IP (only safe if Peregrine is not directly reachable from the internet)
Saved instantly, applies on the next request, no container restart needed.
Below is the minimum config for Nginx / Nginx Proxy Manager (paste in the Advanced tab of the proxy host). Same headers must be forwarded by Traefik / Caddy / HAProxy / etc.
# Headers Laravel needs behind a reverse proxy
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Upload sizing — Livewire uploads, theme assets, plugin uploads
client_max_body_size 100M;
client_body_buffer_size 128k;
proxy_request_buffering off;
# Timeouts for large uploads
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_connect_timeout 60;
# Buffers
proxy_buffer_size 16k;
proxy_buffers 4 32k;Cloudflare in front? Set the SSL/TLS mode to Full or Full (strict) — never Flexible. Flexible terminates HTTPS at Cloudflare and talks HTTP to your origin without a usable trust chain, which breaks signed URLs even with the headers above.
After saving the trusted proxy IPs and reloading the reverse proxy, hit any HTTPS endpoint of your panel. Generated links, password-reset emails, OAuth callbacks and Livewire uploads should all use https://.
The player console (xterm.js) and the live CPU / RAM / disk / network graphs on the server overview are streamed directly from Wings over a WebSocket — the browser connects to your node, not to the panel. Wings refuses any WebSocket whose Origin header isn't on its allow-list, so when Peregrine is served from a different domain than your Pelican panel (it almost always is), the console stays stuck on "connecting…" until you whitelist Peregrine's origin.
On the node (the machine running Wings), open its config file:
nano /etc/pelican/config.ymlFind the allowed_origins key — by default it's empty:
allowed_origins: []Replace it with your Peregrine origin — the URL players load the panel from (scheme included, no trailing slash). Running more than one panel? Add one line per origin:
allowed_origins:
- https://panel.example.com # your Peregrine URL
allow_cors_private_network: true| Line | Why |
|---|---|
allowed_origins |
The exact origins Wings accepts WebSocket / API requests from. Without your Peregrine domain here, the browser's Origin header is rejected and the console never connects. Use the full origin (https://host), one entry per line. |
allow_cors_private_network |
Sends the Access-Control-Allow-Private-Network header. Set it to true when the panel is served over public HTTPS but Wings listens on a private / LAN IP — otherwise modern Chrome blocks those cross-context requests. |
config.yml is only read at boot, so apply the change with:
systemctl restart wingsReload the server page — the console should connect and the live graphs start moving. Still stuck on "connecting…"? Make sure the origin is byte-for-byte identical to your browser's address bar — scheme, subdomain, and no trailing slash.
Peregrine talks to your Pelican panel through a single API key, so all player traffic shares one rate-limit bucket. Pelican caps the Client API at 120 req/min and the Application API at 240 req/min per key — fine for a handful of users, but a busy panel can start hitting 429s even though Peregrine already throttles every user individually.
We ship a tiny Pelican plugin that fixes this cleanly: it exempts your trusted Peregrine IP(s)/hostname(s) from those two limits, while everyone else keeps the normal caps. Recommended once you grow past a handful of active users.
- 📦 Download: Peregrine API Whitelist plugin (
peregrine-whitelist.zip) - 📖 Docs & source:
pelican-plugin/ - Install: Pelican admin → Plugins → Upload the zip → enable → set
PEREGRINE_WHITELIST_IPS→php artisan config:clear.
Small install (≲ a few dozen users)? You don't need it — Pelican's defaults are plenty. It's there for when you scale.
Admin → About & Updates shows the installed version, checks GitHub for the latest panel release (plugin releases are filtered out), and gives you the exact command with a clipboard button.
docker compose pull && docker compose up -dMigrations run automatically on container start when PANEL_INSTALLED=true.
Shipped (latest: v1.0.0-alpha.1):
- ✅ Player React SPA — full Pelican file-manager parity, console, SFTP, databases, backups, schedules, network
- ✅ Filament 5 admin panel with Pelican sync
- ✅ Theme Studio (Vagues 1 + 3 + parity + refinements)
- ✅ Plugin Marketplace + Server Invitations plugin
- ✅ Multi-provider auth (local / Shop / Paymenter / Google / Discord / LinkedIn) + 2FA TOTP
- ✅ Stripe + Pelican webhook bridge for subscription-driven provisioning
- ✅ Docker multi-arch image published to GHCR on every push
Up next:
- 🛠️ Theme marketplace (Theme Studio Vague 4) — share / import / export themes as JSON, fork a preset, public registry mirroring the plugin marketplace pattern
- 🛠️ Token system v2 (Theme Studio Vague 2) — color scales 50→950, Material-style type roles, 5 shadow levels, named gradients, per-section background patterns
- 🛠️ Polish & accessibility (Theme Studio Vague 5) — live WCAG contrast checker, color-blindness simulator, Monaco editor for custom CSS, per-user appearance overrides
Track progress in GitHub issues and milestones.
Multi-arch (linux/amd64 + linux/arm64), published on every push to main and on every panel version tag.
| Tag | Produced by |
|---|---|
ghcr.io/knaox/peregrine:latest |
push to main |
ghcr.io/knaox/peregrine:main-<sha> |
push to main |
ghcr.io/knaox/peregrine:1.2.3 / :1.2 / :1 |
v*.*.* tag |
Workflow: .github/workflows/docker.yml.
| Layer | Choice |
|---|---|
| Backend | PHP 8.3 · Laravel 13 · Filament 5 (Livewire 4) |
| Frontend | React 19 · TypeScript · Vite 6 · Tailwind 4 · TanStack Query · React Router 7 · Motion |
| Database | MySQL 8 (SQLite supported) |
| Cache / Queue | Redis (database fallback) |
| Real-time | Wings WebSocket + xterm.js |
| Container | Docker multi-stage · GHCR · nginx + php-fpm + supervisord |
composer install
pnpm install
pnpm run dev # Vite HMR on :5173
php artisan serve # PHP on :8000
php artisan queue:work # emails + sync jobsFull contributor guide in CONTRIBUTING.md. Internal architecture notes in docs/ (auth, bridge, plugins, Pelican webhooks, queue worker setup).
If you find a vulnerability, do not open a public issue. See SECURITY.md for the responsible disclosure process.
MIT. Self-host freely, fork, modify, resell — no strings.
Peregrine is an independent project and is not affiliated with Pelican, Pterodactyl, or Laravel.
Built on top of Pelican, Laravel, Filament, React, Tailwind, and the open-source community that makes all of this possible.


