The agent, apps & control panels on your VPS
Tutorial 1 showed how to connect an AI to the platform. This one explains what actually lives inside the customer's server: the agent, how apps run, the catalog, custom deploys, and the automatically installed control panels.
Two server personalities #
When a customer buys a VPS or dedicated, the server takes one of two mutually exclusive roles, chosen at checkout:
The two modes are exclusive: a panel server does not run the Impreza agent and has the Docker catalog disabled. You choose at the cart.
What the agent is #
The agent is a small Go daemon we install on the VPS (ID format
agt_xxxxxxxxxxxx). It is the piece that turns "some VPS" into "an Impreza server". In
one sentence:
docker compose, and reports the result back.
What the installer (install.sh) leaves on the server:
- detects OS + architecture (amd64 / arm64);
- installs the Docker engine if missing (the agent uses
docker composefor every deploy); - installs git if missing (used by git-URL deploys);
- downloads the agent binary to
/usr/local/bin/impreza-agent; - installs the systemd unit with hardening (
ProtectHome=yes,NoNewPrivileges, …); - consumes the bootstrap token, receives permanent credentials, writes them to
/etc/impreza-agent/credentials.toml(mode 0600); - starts the
impreza-agent.serviceunit.
From there the agent polls the control plane: when you (or the AI, or the CLI) create a deployment, the order arrives on the next poll and the agent runs it on the server itself. That is why deploys are private: the code runs on the customer's machine, who keeps root. Impreza never executes your app on our infrastructure.
What the agent handles automatically
| Concern | What the agent does |
|---|---|
| HTTPS | Runs Caddy, which issues a Let's Encrypt cert for each HTTP app |
| Domains | Creates/manages routes; allocates a *.imprezaapps.com subdomain when you bring none |
| Tor / .onion | Runs a Tor sidecar, generates the ed25519 keypair, publishes the .onion |
| Builds | git clone --depth=1 + docker build, or a build from an uploaded tarball |
| Ports | Allocates a free host port per instance (HTTP apps sit behind Caddy) |
| Limits | Applies --cpus + --memory per container (default 1 CPU / 512 MB) |
3 ways to run apps (agent mode) #
Catalog (1-click)
The fastest route: apps we packaged and verified. Browse the catalog in My Apps, click Install, pick server + domain, done (~30s). Apps today include WordPress, PrestaShop, n8n, OpenClaw, Vaultwarden, Nextcloud, Matrix, Gitea, SearXNG, Memos, Uptime Kuma, Activepieces, Jellyfin, PhotoPrism, MinIO, Mattermost, Ollama, Open WebUI, RustDesk, Evolution API plus managed databases (PostgreSQL, MySQL, MariaDB, MongoDB, Redis).
You can install the same app multiple times on one server — each instance gets its own host port. (Exceptions: fixed-protocol apps like RustDesk and the databases are one per server.)
curl -X POST https://api.imprezahost.com/v1/platform/deployments \
-H "X-API-Key: imp_..." -H "X-API-Secret: ..." \
-H "Content-Type: application/json" \
-d '{
"app_name": "vaultwarden",
"agent_id": "agt_xxxxxxxxxxxx",
"domain": "vault.example.com",
"onion": true,
"vars": {"signups_allowed": "true"}
}'
Custom app (your own code)
For code that is not in the catalog. Four source modes — the platform auto-detects which from the fields you fill:
- Public image —
{ "image": "nginx:alpine" }. The agent doesdocker pull+docker run. - Dockerfile via local tarball — the CLI tars the dir, uploads, the agent builds on the VPS.
- Dockerfile via git URL —
{ "git_url": "...", "git_ref": "main" }. Private repos:git_auth_method: "deploy_key"(SSH key the platform generates) or"pat"+git_pat(HTTPS token). - Manifest — a full docker-compose YAML, treated like a catalog app.
curl -X POST https://api.imprezahost.com/v1/platform/deployments/custom \
-H "X-API-Key: imp_..." -H "X-API-Secret: ..." \
-H "Content-Type: application/json" \
-d '{
"name": "my-app",
"agent_id": "agt_xxxxxxxxxxxx",
"git_url": "https://github.com/your-user/your-repo",
"git_ref": "main",
"dockerfile_path": "Dockerfile",
"target_port": 8080,
"cpus": 1.0,
"memory_mb": 512
}'
Or from the CLI, in a directory with a Dockerfile:
cd ~/my-app
impreza deploy --agent agt_xxxxxxxxxxxx --name my-app --follow
# → Packaging → Uploading → Deploying → ✓ Deployed at https://my-app-3f7c9a.imprezaapps.com
Auto-deploy on every git push
For git_url deploys, connect a push webhook so every commit on the watched branch
re-deploys (re-clone at HEAD → rebuild → restart in place). Works with any provider:
- GitHub (one-click): paste a fine-grained PAT with
Webhooks: Read & write— we install the webhook and discard the token. - Anything else (manual): connect with no token; we return
{ payload_url, webhook_token }. Add a push webhook to that URL, sending the token as the GitLab "Secret token" or theX-Impreza-Tokenheader.
Enable it from My Apps (Advanced → 🔗 Connect git for auto-deploy), the AI (impreza_git_webhook_connect), or the REST API.
How apps are reached (network modes) #
| Mode | How it is reached | Example |
|---|---|---|
http (default) | URL with HTTPS via Caddy + Let's Encrypt + subdomain | Almost every app |
host_ports | Raw TCP/UDP on fixed ports, no Caddy/cert/domain; response carries connection_info: {ip, ports, public_key} | RustDesk, managed DBs |
hybrid | Raw ports and an HTTP admin UI behind Caddy | AdGuard (DNS on :53/udp + HTTPS admin) |
Managed databases
PostgreSQL, MySQL, MariaDB, MongoDB and Redis ship as catalog apps in host_ports mode —
raw TCP on the VPS public IP, no domain/cert. Deploy returns connection_info: { ip, ports };
connect with the native client (psql, mysql, mongosh,
redis-cli). Credentials surface as ADMIN_USER + ADMIN_PASSWORD
on the My Apps card (click to reveal) — a strong 20-char password is auto-generated.
They ship open to the internet, protected by the password (a per-IP allowlist is on
the roadmap).
Domains, redeploy & .onion (what stays stable) #
- Domains are stable. Once an app has a domain it keeps it — an in-place redeploy never touches it, and even uninstall + recreate under the same name reuses the original
*.imprezaapps.comsubdomain. To change it deliberately,POST /v1/platform/deployments/{id}/domain. - Redeploy in place:
POST /v1/platform/deployments/custom/{id}/redeployrebuilds the current source and swaps the container with near-zero downtime; domain, port and URL never change. Prefer it over uninstall + recreate. - Tor / .onion: any deployment can publish a Tor v3 hidden service alongside its clearnet URL with
onion: true. Onion-only deploys work too (onion: true+ nodomain).
Limits + trust model #
- Cgroups: every custom deploy gets
--cpus+--memoryat start (default 1 CPU / 512 MB), overridable viacpus+memory_mb, clamped against the VPS RAM. - Build context size: tarball uploads cap at 100 MB. Use
.dockerignore(the CLI already excludes.git,node_modules,__pycache__, …). - API rate limit: 60 req/min/key default (raise per-key in clientarea).
- Trust: we trust the customer — the VPS is yours, the agent runs your code, cgroup ceilings prevent runaway containers. We do not scan images/Dockerfiles today.
Automatic control-panel installation #
This is the alternative to the agent. Instead of the agent + Docker flow, the customer picks a classic hosting panel at checkout that owns the whole server — managing sites, databases, email, DNS and SSL from the panel's own UI.
Available panels
| Panel | Cost | Admin port | Notes |
|---|---|---|---|
| aaPanel | free | randomized (bt default prints it) | Lightweight; optional 1-click stack (nginx + PHP 8.4 + MariaDB). Tolerant of a non-pristine OS. |
| CyberPanel | free | 8090 | OpenLiteSpeed base (LSCache, 1-click WordPress, PHP, MariaDB, DNS, email, SSL). Needs a fresh OS. |
| CloudPanel | free | 8443 | Modern nginx for PHP, Node.js and Python with free SSL. Needs a fresh OS. |
| cPanel/WHM | license NOT included | 2087 | Installs unlicensed; log in to WHM as root and start the 15-day trial or apply your own IP-bound license. |
OS compatibility (important)
Each panel runs only on certain operating systems. The cart validates this up front and, if the server OS is not compatible, asks you to reinstall with a supported one first:
- aaPanel: Ubuntu 20.04/22.04/24.04, Debian 10/11/12, CentOS 7/8, AlmaLinux 8/9, Rocky 8/9.
- CyberPanel: Ubuntu 20.04/22.04/24.04, Debian 11/12, AlmaLinux 8/9, Rocky 8/9, CentOS 7/8.
- CloudPanel: Debian 11/12/13, Ubuntu 22.04/24.04.
- cPanel/WHM: AlmaLinux 8/9/10, CloudLinux 8/9/10, Ubuntu 24.04 only.
/etc/os-release at boot and
aborts the install on an incompatible OS instead of half-installing.
The flow, from checkout to "ready"
- At the cart, pick the panel tile. (For aaPanel there is also a bare profile = panel only, fast; or full = panel + nginx/PHP/MariaDB stack.)
- On a VPS, the install runs automatically a few minutes after payment. On a dedicated, our team provisions it once the hardware is racked.
- The OS gate runs on the server; if incompatible, the install aborts and the clientarea shows a "reinstall with a supported OS" card.
- When done, the panel appears in My Apps with the login URL, the username and the password (click to reveal — stored encrypted on our side).
- cPanel/WHM exception: no own password — log in to WHM (
:2087) with the server's root account, accept the EULA, then start the trial or apply your license.
Reinstall / leave panel mode
- Reinstall is self-service from the clientarea (the OS list is filtered to compatible ones). Useful if the first attempt failed on an incompatible OS.
- Uninstall exists as an exit route — to leave panel mode and go back to a bare server.
Which mode should you choose? #
| You want to… | Choose |
|---|---|
| Ship your own Docker / code / git apps | Agent mode |
| Deploy by chatting with an AI | Agent mode + MCP |
| Ready-made catalog apps (WordPress, n8n, Vaultwarden…) | Agent mode (1-click catalog) |
| A classic hosting panel (sites/email/DNS via UI) | Panel mode (aaPanel / CyberPanel / CloudPanel / cPanel) |
| Host many client sites with cPanel | Panel mode (cPanel/WHM — bring your license) |
