Veil is a management panel for NaiveProxy, Hysteria2, olcRTC, and Mieru. It installs the Panel first; proxy Inbounds are added later from the Panel.
Upgrading to v0.7.3? Read the upgrade guide first: the state migration requires a pre-upgrade backup for downgrades.
One command, answer a few questions, done:
curl -fsSL https://github.com/mikkelchokolate/Veil/releases/latest/download/install.sh | shTo install the current main commit instead of a tagged release (builds from source; not cosign-signed):
curl -fsSL https://raw.githubusercontent.com/mikkelchokolate/Veil/main/scripts/install-main.sh | shinstall.sh runs without root. It downloads and verifies the pinned verifier,
install-privileged.sh, the Veil archive, signed checksums, and signed
provenance. Only after every privileged payload passes verification does it
invoke sudo itself.
The installer asks for the Panel exposure mode (local, direct, or caddy)
and whether to choose a random high Panel port or use a port you enter. Veil
still installs as a panel-only control plane first; you do not need a domain
or Caddy unless you choose Caddy Panel access or add a NaiveProxy Inbound later.
For local access, the installer prints the Panel's self-signed HTTPS address:
Panel access: https://127.0.0.1:<panel-port>/<web-base-path>/
or, if you choose Caddy Panel access:
Panel URL: https://vpn.example.com/a1b2c3d4e5f6/
The HTTPS Panel URL path is randomly generated — only you know it. Local access uses a self-signed certificate. Direct mode attempts a trusted IP certificate and falls back to a self-signed certificate with a warning if issuance fails.
Panel-only local access:
curl -fsSL https://github.com/mikkelchokolate/Veil/releases/latest/download/install.sh | sh -s -- \
--panel-access local \
--yesPanel HTTPS access through Caddy on a domain, without exposing the Panel port:
curl -fsSL https://github.com/mikkelchokolate/Veil/releases/latest/download/install.sh | sh -s -- \
--panel-access caddy \
--domain vpn.example.com \
--email admin@example.com \
--yesProtocol runtimes are not selected during install. Open the Panel after install and add NaiveProxy, Hysteria2, olcRTC, or Mieru as Inbounds.
Veil expects Panel user/session authentication before any public exposure. An
unconfigured local install opens a loopback-only first-run page where you
create the initial administrator and confirm backup responsibilities. The
setup API is disabled for direct, caddy, and non-loopback listeners.
For planned public exposure, initialize credentials before changing the access mode:
sudo veil admin reset
# or set your own account
sudo veil admin set --username admin --role admin # prompts for the password without echo
# non-interactive: read it from stdin or a file instead of argv
printf '%s\n' "$PANEL_PASSWORD" | sudo veil admin set --username admin --role admin --password-stdinWhen veil serve listens on a non-loopback address, it refuses to start unless
an API token, at least one Panel user, authenticated metrics, and TLS are
configured. Caddy exposure also requires a Panel user and authenticated metrics
before the reverse proxy is allowed to expose the loopback listener.
Open the Panel and add Inbounds for the protocols you need:
| Protocol | Transports | Notes |
|---|---|---|
| NaiveProxy | TCP | Requires Caddy, domain, email, username, and password |
| Hysteria2 | UDP | Does not require Caddy; real ACME cert when given a domain |
| olcRTC | UDP | Does not require Caddy |
| Mieru | TCP or UDP | Does not require Caddy |
TCP and UDP can use the same numeric port at the same time. For example, mieru tcp 443 and mieru udp 443 are different transport bindings and can coexist.
Passwords are auto-generated when omitted. You can replace them in the Panel.
Run sudo veil in a terminal to open the management and recovery menu, or
sudo veil menu to open it explicitly. It manages local services, logs,
credentials, backups, updates, installation, and selected protocol runtimes even when the
web Panel is unavailable. Changes require confirmation in the menu.
veil help prints the full catalog. veil help <command> shows flags for one command.
| Command | Purpose |
|---|---|
veil help |
Show every command, including nested subcommands. |
veil menu |
Open the terminal management and recovery menu; works without the web Panel. |
veil install |
Install Panel access and credentials; protocol runtimes are acquired on first use (Caddy is acquired immediately when needed to expose the Panel). |
veil serve |
Run the HTTP API and Web Panel. |
veil status |
Show managed service status (--json for machine output). |
veil doctor |
Run host readiness checks (required/optional commands, protocol DNS reachability). |
veil admin reset |
Reset the administrator with new random credentials. |
veil admin set |
Set or update a user (--username, --role admin|viewer; password via interactive prompt, --password-stdin or --password-file; legacy --password is visible in the process list). An existing user keeps their TOTP factor, recovery codes and passkeys; add --clear-2fa to remove them when the authenticator is lost. |
veil admin show |
List registered users and roles. |
veil admin rotate-key |
Rotate the AES state key and re-encrypt state. |
veil config validate |
Validate a Management state file without starting a server. |
veil runtime install |
Preinstall or repair protocol runtime binaries (--only, --bin-dir); normally acquired on first use. |
veil backup create |
Create an encrypted archive of Panel state and the encryption key. |
veil backup list |
List managed backup archives. |
veil backup verify |
Decrypt and verify a backup without writing state. |
veil backup restore |
Restore state and key from a backup. |
veil backup prune |
Apply daily/weekly/monthly retention (--dry-run to preview). |
veil backup schedule enable |
Store the passphrase and enable the daily backup timer. |
veil backup schedule disable |
Disable the daily backup timer. |
veil repair |
Repair managed generated files without arbitrary side effects. |
veil rollback list |
List configuration-file backups. |
veil rollback restore |
Restore files from a configuration backup. |
veil rollback cleanup |
Remove a configuration backup after a successful restore. |
veil update |
Download and install the latest Veil release (--yes --staged). |
veil uninstall |
Remove the Panel, services, configuration, and state. |
veil version |
Print the Veil version (--check to compare with the latest release). |
Everyday examples follow. For v0.7.2 → v0.7.3, use the dedicated upgrade sequence instead of the generic staged-update example: binary rollback cannot undo its migration.
veil status
veil version --check
veil update --yes --staged
veil uninstall --yes # also removes config/state; reinstall starts fresh
veil uninstall --yes --keep-data # preserve credentials and config across a reinstallPrivilege separation: the Panel runs as the locked veil user with no
capabilities. Root-only host mutations go through the allowlisted
veil-helper.socket.
The Panel records authentication, user, configuration, apply, and service
actions in a structured, redacted, rotated JSONL audit log. With the default
state path it is stored at /var/lib/veil/audit/panel.jsonl. Encrypted backup
directories must be writable by the process performing each operation.
sudo veil backup create --passphrase-file /root/veil-backup-passphrase \
--output-dir /var/lib/veil/backups
sudo veil backup schedule enable --passphrase-file /root/veil-backup-passphrase
sudo veil backup list --dir /var/lib/veil/backups
sudo veil backup prune --dir /var/lib/veil/backups \
--daily 7 --weekly 4 --monthly 12 --dry-run
sudo veil backup verify /var/lib/veil/backups/veil_backup_YYYYMMDD_HHMMSS_NNNNNNNNN_RANDOMHEX.tar.gz.enc \
--passphrase-file /root/veil-backup-passphrase
sudo veil backup restore /path/to/archive.tar.gz.enc \
--passphrase-file /root/veil-backup-passphrase --check-only
veil repair --backup-dir /var/lib/veil/backups --audit-log /var/log/veil/audit.jsonl --yes
veil rollback list --backup-dir /var/lib/veil/backups
veil rollback restore <backup-id> --backup-dir /var/lib/veil/backups --yes
veil rollback cleanup <backup-id> --backup-dir /var/lib/veil/backups --yesveil rollback restore accepts the managed /etc/veil, /var/lib/veil and
installed unit directory trees by default, which is exactly what install and
repair snapshot, so the commands above restore the unit files too. Inside the
unit tree only names starting with veil are accepted — veil*.service,
veil*.socket, veil*.timer and their <unit>.d/*.conf drop-ins — so a
manifest cannot introduce an arbitrary unit name there. The name filter follows
the resolved default unit root and is not applied to a tree added with
--restore-root (that one is bounded by the root allowlist alone, so only add
roots whose contents you expect). That default root is the directory the
installed veil.env records (VEIL_SYSTEMD_DIR), or /etc/systemd/system when
it records none, so a custom --systemd-dir install restores its units without
extra flags while that file is intact. If it no longer records the directory —
an interrupted install that overwrote veil.env is the usual case — the paths in
the backup fall outside the defaults and the restore refuses (outside the allowed roots): add the tree that holds them with --restore-root, which
replaces the defaults, and repeat it for every root the backup touches. Any other
managed root that a custom install moved also needs --restore-root.
A configuration backup is a trusted-input archive: it carries state.key,
veil.env and the systemd units, and the restore writes them as root. Restore
only backups from a source you trust.
- Installation — access modes, flags, and compiling from source
- Troubleshooting — diagnostics, logs, and rollback
- Panel operations — live validation and apply previews
- Hardening — deployment hardening and signed releases
- Disaster recovery — encrypted backups and restore
- Known limitations
- Changelog
- Security policy
