# PasarGuard panel: a self-hosted proxy management dashboard for Xray-core and WireGuard

> PasarGuard bundles a FastAPI backend, a React dashboard and a CLI for running hundreds of proxy accounts across several nodes. It installs from a shell script, ships under AGPL-3.0, and its own README leaves several operational questions open.

**PasarGuard/panel** — PasarGuard | Unified GUI Censorship Resistant Solution

- Repository: https://github.com/PasarGuard/panel
- Website: https://docs.pasarguard.org
- Stars: 2,609 · Forks: 560
- Language: TypeScript
- License: AGPL-3.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/pasarguard-panel

## What PasarGuard panel actually manages

PasarGuard is a control plane, not a proxy protocol. The README describes it as a "proxy management tool" built with Python and React.js that handles "hundreds of proxy accounts" through a web interface. The thing doing the proxying is Xray-core or WireGuard, both named as supported backends. PasarGuard sits in front, creates the accounts, attaches limits to them, and hands users a subscription link.

The audience is narrow and identifiable. Someone running a single VPS with one inbound does not need this; the README's own framing is large-scale management and multi-node distribution. The features list is aimed at operators who resell or share access: traffic and expiry date limits, periodic limits that reset daily or weekly, HWID and device limits, multi-admin support with RBAC for "granular permissions and scoped access". Those are the concerns of a service with paying or semi-public users, not of a personal tunnel.

Protocol coverage is broad on paper. Vmess, VLESS, Trojan, Shadowsocks, WireGuard and Hysteria2 are all listed, with TLS and REALITY support, plus multi-protocol for a single user. The README does not explain how much of that is native to PasarGuard and how much is configuration passed through to Xray-core, which matters if you plan to debug a protocol problem.

## How the panel, the API and the workers fit together

The repository layout tells you more than the README does. There is app/ for the FastAPI application, dashboard/ for the React frontend, cli/ and a pasarguard-cli.py entry point, plus separate top-level files named node_worker.py and scheduler_worker.py. That is a process split: the API serves the dashboard and REST endpoints, the scheduler runs periodic work (the README mentions periodic traffic limits and system monitoring), and the node worker talks to remote proxy nodes.

pyproject.toml confirms the stack. FastAPI with uvicorn, SQLAlchemy with asyncio, alembic for migrations, aiogram for the Telegram bot, apscheduler for scheduling, and pasarguard-node-bridge plus nats-py for node communication. NATS is not incidental: the .env.example notes that for a multi-worker all-in-one deployment you set UVICORN_WORKERS above 1 together with NATS_ENABLED=1. So scaling the API process means introducing a message broker.

The same file shows a role system mid-transition. ROLE is documented as all-in-one, with backend, node and scheduler marked deprecated and scheduled for removal in 7.0.0. The current version is 5.4.1, so anyone building tooling around the separate roles is building on something the project has already announced it will delete. The README never mentions this; only the example environment file does.

## Installing PasarGuard with the official script

The README gives one installation path: a shell script pulled from the PasarGuard/scripts repository and executed with sudo. The database is chosen with a flag. TimescaleDB is labelled recommended, SQLite is the fallback when no flag is passed.

```bash
sudo bash -c "$(curl -fsSL https://github.com/PasarGuard/scripts/raw/main/pasarguard.sh)" @ install --database timescaledb
```

Running that command installs the panel with a TimescaleDB backend. To use SQLite instead, drop the flag; MySQL, MariaDB and PostgreSQL have their own values for --database. Note that this pipes a remote script straight into a root shell, so read the script before running it on a machine you care about.

After installation the README says files land in /opt/pasarguard, configuration lives in /opt/pasarguard/.env, and data goes to /var/lib/pasarguard. The dashboard is served on port 8000 and the README states plainly that it requires an SSL certificate, pointing at a separate guide for issuing one. For a quick look without a domain, it suggests forwarding the port over SSH:

```bash
ssh -L 8000:localhost:8000 user@serverip
```

With that tunnel open, http://localhost:8000/dashboard/ works in your browser. The README warns this is testing only and that access disappears when the terminal closes.

The first real task is claiming the owner account. The README's next-steps block gives the command:

```bash
pasarguard cli generate-temp-key
```

That produces a one-time setup key, which you enter on the dashboard login page to create the owner account. `pasarguard --help` lists the rest of the CLI. The README does not document what happens if the key expires before you use it.

## Running it in Docker and what the compose file assumes

The repository includes a Dockerfile and a docker-compose.yml, and the README links a Docker Hub image at pasarguard/panel. The compose file is four lines of substance: it pulls pasarguard/panel:latest, restarts always, reads .env, mounts /var/lib/pasarguard, and sets network_mode: host.

```yaml
services:
  pasarguard:
    image: pasarguard/panel:latest
    restart: always
    env_file: .env
    network_mode: host
    volumes:
      - /var/lib/pasarguard:/var/lib/pasarguard
```

Two things follow from that. Host networking means the container binds directly to the host's port 8000, so port mapping in compose is neither used nor possible here. And the image tag is latest, not a pinned version, so a restart can pull a different build than the one you tested. If you want reproducible deployments you will have to pin the tag yourself; the compose file does not.

The Dockerfile builds with uv against Python 3.14 and copies two wrapper scripts into the image, /usr/bin/pasarguard-cli and /usr/bin/pasarguard-tui. That means the CLI is available inside the container even though the README's quick-start section only shows it on a script-installed host.

## Where PasarGuard gets in your way

The Python requirement is the first hard constraint. pyproject.toml sets requires-python to ">=3.14". If your distribution does not ship 3.14 or you cannot install it, the Docker path is effectively your only option, and the compose file uses host networking and an unpinned image. That is a real trade-off, not a detail.

TLS is treated as mandatory rather than optional. The README states the dashboard requires an SSL certificate and links out for instructions. The .env.example does show UVICORN_SSL_CERTFILE and UVICORN_SSL_KEYFILE, so HTTPS is configured at the uvicorn layer, and it also offers UVICORN_UDS for running behind a local reverse proxy. The README's quick start does not describe that reverse-proxy setup, so if you terminate TLS elsewhere you are reading the example file, not the guide.

The README is also silent on several things an operator will hit. There is no documented backup or restore procedure for /var/lib/pasarguard, no rollback instructions for a failed upgrade, and no stated migration path between database backends after installation. The one upgrade signal in the repository is the deprecation notice in .env.example, which says the backend, node and scheduler roles are removed in 7.0.0 while the current release is 5.4.1. That is a future breaking change announced in a config file rather than in the README.

Finally, the license is AGPL-3.0. If you modify PasarGuard and let users interact with it over a network, the AGPL's source-availability condition is the thing to read, not this article. Running it unmodified as a service is a different situation from forking it into a product.

## Compared with running Xray-core and a subscription script by hand

The obvious alternative is not another panel; it is the thing PasarGuard wraps. Xray-core is a proxy server with a JSON configuration file. You write inbounds and clients by hand, restart the service, and distribute connection strings yourself. That approach has no database, no web process, no scheduler and no broker. It also has no traffic accounting, no expiry dates, no HWID limits and no subscription endpoint that Clash or V2ray clients can fetch.

The difference is where state lives. With hand-written configuration, the config file is the source of truth and you edit it. With PasarGuard, the database is the source of truth, the panel writes Xray configuration from it, and the file becomes an output. That inversion is what makes hundreds of accounts tractable and what makes a database backup the thing you must not lose. It also means a database problem is a proxy outage, which is not true of a static config file.

If your requirement stops at "one server, a handful of users, no billing", the hand-written route is less machinery and fewer failure modes. PasarGuard earns its complexity when account lifecycle, quotas and client-compatible subscription links are the actual work.

## Who should run PasarGuard panel, and what to check first

Adopt it if you already run Xray-core or WireGuard and the manual work of creating accounts, tracking quotas and regenerating subscription links has become the bottleneck. The feature set is coherent for that job: multi-inbound on a single port with fallbacks, subscription links for V2ray, Clash and ClashMeta, a REST API, a Telegram bot and a CLI. The repository is not archived and the last push was on 2026-09-24, with v5.4.1 released on 2026-09-12.

Do not adopt it if you need a vendor to call when the panel is down, or if you cannot read Python when a migration fails. The README routes everything past installation to docs.pasarguard.org, and the repository does not cover backup, rollback or backend migration. It is also the wrong tool for a single personal tunnel.

Before you deploy, confirm three things. That Python 3.14 is available or that you are committing to the Docker path with a pinned image tag rather than latest. That you have chosen between SQLite and TimescaleDB deliberately, since the README calls TimescaleDB recommended but the installer defaults to SQLite. And that you have read the ROLE deprecation note in .env.example, because anything you build on the backend, node or scheduler roles has a removal date attached to it in 7.0.0.

## Conclusion

Adopt PasarGuard if you already operate Xray-core or WireGuard nodes and want a single dashboard plus REST API for accounts, traffic limits and subscriptions, and you are willing to run it on a host with a real TLS certificate. Do not adopt it if you need a vendor-supported product or cannot read the source when something breaks, because the README points at docs.pasarguard.org for anything beyond installation. Before deploying, verify that the uv-based install resolves under Python 3.14 on your distribution and confirm which database you want, since the installer defaults to SQLite while the README labels TimescaleDB as the recommended option.

## FAQ

### How do I install PasarGuard panel?

The README gives a single command that downloads a script from the PasarGuard/scripts repository and runs it with sudo, passing @ install plus a --database flag for timescaledb, mysql, mariadb or postgresql. With no flag it installs with SQLite. Files land in /opt/pasarguard and data in /var/lib/pasarguard.

### Which database should I pick for PasarGuard panel?

The README labels TimescaleDB as recommended and shows it first, while SQLite is what you get when you pass no --database flag. PostgreSQL, MySQL and MariaDB are also listed as options. The repository does not describe how to move between backends after installation.

### Does PasarGuard panel need a TLS certificate?

Yes. The README states the dashboard requires an SSL certificate and links to a separate guide for issuing one, with access at https://YOUR_DOMAIN:8000/dashboard/. It offers SSH port forwarding on port 8000 for testing without a domain, and warns that access is lost when the terminal closes.

### How do I create the first owner account in PasarGuard panel?

Run pasarguard cli generate-temp-key to produce a one-time setup key, then enter that key on the dashboard login page to create the owner account. The README does not say what happens if the key is not used before it expires.

### What protocols does PasarGuard panel support?

The README lists Vmess, VLESS, Trojan, Shadowsocks, WireGuard and Hysteria2, with TLS and REALITY support and multi-protocol for a single user. Proxying is handled by Xray-core or WireGuard, which the README names as the supported backends.

## Sources

- [License: AGPL-3.0](https://github.com/PasarGuard/panel/blob/main/LICENSE)
- [PasarGuard/panel on GitHub](https://github.com/PasarGuard/panel)
- [Project website](https://docs.pasarguard.org)
- [README](https://github.com/PasarGuard/panel/blob/main/README.md)
- [Releases](https://github.com/PasarGuard/panel/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pasarguard-panel
