Repository navigation
[2.1.2] - 2026-10-14
Tokens limited to selected projects, backups and database copies for classic projects, and engine fetches that cannot reach internal addresses.
An important note for current users: if you’re using PanelAlpha together with the Engine, please stay on your current version for now. Making the Control Panel ready for 2.0 is the next milestone on our list, and work on that is already in progress. Thanks for sticking with us for this next step!
After 2.1.1 changed how redeploys, the firewall and project isolation work, 2.1.2 is about access, data and safety. An API or MCP token can be limited to selected projects, a project can be protected from token calls, and an allowed project can call the Engine API itself. Classic projects get backups, backup schedules and database copies in staging, clone and push. Every URL the engine fetches is checked against internal addresses. A project's deploy history, environment, files and PHP errors can be read over the API and MCP, and a long list of fixes covers the firewall migration, cancelled deploys, backups and site databases.
Install on a fresh Debian or Ubuntu VPS and deploy your first application:
curl -fsSL https://get.panelalpha.com/engine | sh -s -- --repo https://github.com/n8n-io/n8nReplace the repository URL with your own, or leave out -s -- --repo … to install just the engine.
Read the announcement: PanelAlpha Engine 2.1.2
Documentation: panelalpha.com/documentation/panelalpha-engine · Discord
Added
Secrets and access
- Project-scoped tokens.
POST /mcp-tokenstakesprojects, and the token then reaches only those projects.PUT /mcp-tokens/{id}/projectschanges the list at once. Project and task lists are filtered, other global routes answer 403, and MCP does not list tools the token cannot call. A scoped token cannot change the project's resource limits, PHP and Redis sizing, or its suspension; those stay with the operator. The MCP activity log records the project a call named and filters by?project=. Tokens without a project list behave as before. - Protected projects.
pae project:settings:set protected on --project=<name>makes every API or MCP token call that would delete, stop or redeploy the project answer 409project_protected. Nothing runs and nothing is queued. Your ownpaecommands are not affected, and a token cannot turn the setting off. - Engine API access for projects.
pae system:api-access:allow <project>lets a container project call the Engine API, with a scope ofself,allor a list of projects, and--mcpfor MCP. On every deploy the app receivesPANELALPHA_ENGINE_URL,PANELALPHA_ENGINE_TOKEN, andPANELALPHA_ENGINE_CAwhen the certificate is not trusted for that name. The firewall opens port 2011 for that project only.deny,listandrotatemanage the entries, and a rotated token reaches the running app without a rebuild. - End SFTP sessions. End the open sessions of one SFTP account or of all accounts in a project, over the API or with
sftp_session_killandsftp_session_kill_all. Deleting an SFTP account ends its sessions first.
Security
- Egress guard. Every URL the engine fetches for you, including git clone and ls-remote,
files/fetch, MCPfile_urland vault paste checks, is refused with 422host_refusedwhen it resolves to loopback, link-local, private ranges, or the engine's own and Docker networks. Each redirect is checked again, and a download connects to the address that was checked, so a DNS change in between cannot redirect it. A git token is sent only to the repository's own host. To fetch from a private git or file server, setEGRESS_ALLOW_HOSTSorEGRESS_ALLOW_PRIVATE.
Deploy
- Deploy history.
deploy_list,GET /projects/{username}/deploysandpae project:deploy:listlist the deploys the engine keeps for a project, newest first: status, install or redeploy, source, branch, commit, times, and the task or webhook that ran it.deploy_log_readreads the log of any of them. The engine keeps 10 deploy logs per project by default (DEPLOY_LOG_KEEP). - Attach a git source. A container project created without a repository can take one later with
project_source_attachorPOST /projects/{username}/source. The repository and its token are checked the same way as on create, and the first deploy is queued as a task. A project that already has a source or already runs an app is refused. - Branches and tags.
source_refslists a repository's default branch, branches and tags without cloning it, before any project exists. Private repositories take a token or avault:reference. - Environment variables.
env_var_listshows a container project's stored variables, with values masked.env_var_setadds, changes or removes them and recreates only the services that changed, without a rebuild. Variables read at build time still need a rebuild. - Archive deploy from the CLI.
pae project:deploy-archivedeploys a local.zipor.tar.gzinto a container project created without a git repository, and prints the log as it runs. The archive is removed afterwards, whether the deploy worked or not. - Default tunnel.
pae settings:set default_tunnel none, or the engine-address section ofpae configure, gives new projects created withouttunnela name under the engine's own domain instead of a PanelAlpha Online tunnel.panelalphaor an unset value keeps the previous behaviour./system/infoshows the setting. - Preview tunnels. A tunnel takes
purpose(defaultorpreview) andttl_minutes(5 to 43200). A PanelAlpha Online tunnel created without a hostname gets a freepreview-<id>.panelalpha.onlinename. A preview tunnel lives 24 hours unless you give itttl_minutes, and expired tunnels are removed every five minutes. A WordPress site may still redirect a preview name to its own domain.
Backups
- Classic-project backups. Classic projects now also back up and restore: the home directory and one dump per project database. A backup can take only files or only databases, with a note. A restore swaps the home in with rollback, or extracts over it, and never extracts as root into a directory the account can change. A classic backup does not include the project's PHP configuration.
- Backup schedules.
GET,PUTandDELETE /projects/{username}/backup-schedule, orpae project:backup:schedule, keep one UTC cron schedule per project, at most hourly, withkeep_lastfrom 1 to 365. A busy project is retried for an hour. Only scheduled backups are pruned. - Backup download.
backup_item_downloadandpae project:backup:downloaddownload one item of a backup from local, S3, FTP or SFTP storage.
Databases
- Database copies. A classic staging copy or clone now also copies every database the source owns, with new users and passwords and the same grants (
copy_databases, on by default). Copied routines, triggers, views and events run as a project user, not as root. The copy serves the copied site instead of the engine placeholder. A push takesfilesanddatabases, both on by default, takes a rollback dump of each target database first, and keeps the target's users and passwords. Cancelling a queued push leaves the target free for other work. - Push selected tables. On a classic project, a push can replace only the tables you name, each after a rollback dump.
project_push_difflists the tables whose structure or data differ between the source and the target. - Database rename.
mysql_database_renamerenames a project database and moves its grants. The old database is dropped only after the new one is complete. If the rename fails, the old database and its grants stay as they were.
Files and diagnostics
- Directory listing.
file_listlists a directory as the project's account: type, size, modification time, mode, owner and link target, up to 20000 entries. A path through a link out of the home is refused. - Image thumbnails.
GET /projects/{username}/files/thumbnailreturns a WebP thumbnail of a PNG, JPEG, GIF or WebP file, 16 to 1024 px, with caching headers. The format is read from the file's content, so an SVG named.pngis refused. Files over 50 MB or 50 megapixels are refused. - PHP error log. Classic PHP hosting writes PHP errors to a log per project, rotated at 10 MB.
php_error_log_getreads the last lines, up to 5000. On a container project it reads the app container's output. The update adds the setting to existing projects, and it takes effect when the project's PHP handler next restarts. - Task list.
task_listlists tasks newest first, filtered by project, status, action or time, so a client can tell whether anything is running on a project. By default a token sees its own tasks and the tasks the engine started itself. A push now runs as a task and answers with itstask_id. - Disk usage.
metrics:projects:diskmeasures every project every 15 minutes, in the background, and keeps 30 days. Each project in the API carries adiskblock with files, Docker data, total and limit. Project usage now includes the project's Docker data (images, build cache, containers and volumes), which is usually most of its disk.
Changed
Deploy
- Git submodules. Submodules are fetched on every git deploy, including a pull, a branch change or a webhook redeploy, not only on the first clone. A submodule that cannot be fetched now fails the deploy with git's own reason; it used to only warn.
git: {submodules: [...]}in the app config names the only submodules to fetch. - Cancelled deploys. A cancel ends the deploy and its task as cancelled, also while cloning and during the final health check, and never takes down a version that was live. A cancelled redeploy removes only the containers and images it created, and an app that was stopped stays stopped. A staging copy cancelled while queued is removed.
- Clearer failure messages. When a dependency such as a database fails its health check, the deploy failure quotes what the health check answered. The create task records the id of its first deploy while it runs.
- Zero-downtime switch. Moving traffic to the new version reloads the webserver and never restarts it.
- Volumes, ports and secrets. Empty volume lists, bracketed IPv6 port publishes, and two paths that map to the same volume name are handled. Submodule URLs are checked the way git resolves them, and internal addresses are refused.
Security
- Engine API. Port 2011 refuses
/api/internal/, also when the request goes through/index.php. - File fetch and unzip. Unzip checks every archive entry the same way a deploy checks its archive, and reads the archive as the account instead of root. A fetch over the size limit leaves nothing behind.
- Secrets on disk. Temporary copies that can hold secrets are removed even when a step fails. A project
.envis never made more permissive than it was, and nested copies are owner-only.vault:references are resolved in every secret field, and inenv_varsof an archive deploy. Scripts download only over https, also after a redirect. - Git and webhooks. Manual git operations are refused while a deploy, backup or restore holds the project. A push on a checkout managed by deploys is refused. A signed webhook body is acted on once, and a GitHub delivery checked against an empty secret is refused. A
git_connectproject can be disconnected again. - SFTP. Each generated SFTP login has its own sshd rules: chroot, SFTP only, no forwarding. The SFTP image is pinned to a fixed build.
Firewall
- ufw changes. A ufw change whose rules fail to load is rolled back. If setup fails, ufw is turned off instead of leaving the host closed.
- CSF migration. Before CSF is removed, its stored rules are test-loaded. CSF stays in place if any rule would be refused.
- Rule checks. Ports with leading zeros, outside 1 to 65535, or in reversed ranges are refused. A deny rule covering port 2011 is refused. A deny rule covering your own address on SSH or on every port needs
confirm, and one that overlaps web, FTP or SFTP ports returns a warning. The trusted list refuses ranges wider than /8 (IPv4) or /16 (IPv6). A rule clash answers 422, a lock timeout 409, a failed command 502. - fail2ban. A new jail bans addresses after repeated wrong site passwords. The engine API jail no longer bans an address for a single 401 after earlier bans.
Backups and databases
- Site database dumps. Dumps and restores of site databases run in the sites database service. This fixes backups that held empty dumps.
- Backup and restore locks. Backup and restore hold the project's deploy lock, so they do not run during a deploy. A restore gives
~/projectback to the account. A local backup store must be a directory the engine keeps across restarts. - MySQL. Renaming a user no longer doubles the prefix. After a password rotation the engine stores the new app database password. Databases with a hyphen in the name can be backed up and restored.
- phpMyAdmin. Single sign-on is refused for a suspended project.
Domains and proxy
- Domains. A subdomain created over the API records its parent, and a project's main domain cannot be deleted. A domain rename that fails partway restores routing.
- Proxy rules. Two HTTP rules on the same name and port are refused. Rules with wildcard names now work, and wildcard rules on ports 80 and 443 listen on the host's addresses.
- ModSecurity audit log. Downloads are paged and also read rotated gzip files.
- SSL settings. Clearing an SSL setting leaves it unset instead of an empty value.
Runtime
- sysbox recovery. A project account that sysbox left unmapped is recreated when the app or a service starts, or when sysbox refuses to start it, under the project's deploy lock. A periodic sweep also fixes stopped accounts.
- Tasks. The first final status of a task wins. A task cancelled after its worker died is removed from the job queue, so it does not run later. Task log streams are capped, and the scheduler lock expires and is cleared when the engine starts.
API and MCP
- Validation. Validation errors name the field. A field of the wrong type answers 422 instead of 500.
- Responses. A second engine update while one runs answers 409. An FTP quota of 0 or none is reported as unlimited. An account without its own UID and GID answers 409 instead of running as www-data.
- Files.
files/statreturns the same entry shape asfile_listinsidedata, and describes file names with spaces correctly. This changes its response. - MCP. Null arguments get their defaults.
file_downloadis not offered to read-only tokens. Tool errors are cleaned before they reach the activity log. Cursor can be connected with an install link: the engine URL and token go through Cursor's install dialog, not through the chat. pae project:delete. Needs--forcewhen it does not run in an interactive terminal.
Install and update
- Update status.
latest_updatereports an unfinished update as failed, with the reason, and a dead start no longer blocks the next update. The updater uses https only. - Mail. An Exim rebuild fails when a config path cannot be written. Smarthost credentials are written as given, and mail settings are merged on save.
- IP addresses. A subnet the host cannot route is refused, and an assigned address is checked on the interface.