Skip to main content
This page brings together, in a single document, everything Vertra Cloud does and with what limits — plans and pricing, supported languages and databases, MCP, snapshots, workspaces, API, error codes and fixes for the most common problems. It is the source from which the other pages in this documentation are derived, and it was written to be read by both people and AI assistants.
Mirror: plans-and-limits publishes the plan, runtime, database, Data tab and deploy limit tables from this page in summarized form. Changed a number here? Update plans-and-limits, overview and api-reference/limitations in the same pass.

1. Overview

Vertra Cloud is a Brazilian cloud hosting platform for deploying applications and managed databases. It supports 13 languages, 4 database types, real-time monitoring, custom domains, an MCP server for your AI agent, collaborative workspaces and a VSCode extension. SLA: 99.99% availability, on every plan. This is the only SLA number the platform states — marketing, plan cards, the Enterprise page, the Terms of Use and the public docs all say 99.99% and nothing else.
  • Site: https://vertracloud.app
  • Dashboard: https://vertracloud.app/dashboard
  • API: https://api.vertracloud.app
  • Documentation: https://docs.vertracloud.app
  • CLI (guide): curl -fsSL https://cli.vertracloud.app/install | sh on macOS/Linux, irm https://cli.vertracloud.app/install | iex on Windows (PowerShell)
  • MCP server (guide): remote endpoint https://mcp.vertracloud.app/mcp (OAuth), or local with npx -y @vertracloud/mcp and a VERTRA_API_KEY
  • SDKs (guide): npm install @vertracloud/sdk-api · pip install vertracloud-api · go get github.com/vertracloud/sdk-api-go
  • VS Code extension (guide): code --install-extension VertraCloud.vertra-cloud
  • GitHub Actions (guide): vertracloud/github-action

2. Supported Languages

Versions are updated regularly with security updates. A version that reached end of support is removed from the list. If the version saved on an application is no longer available, the platform uses the newest release of the same line (for example, 3.11 runs on 3.11.17); if the whole line was removed, it falls back to the recommended version.

Language detection

The platform detects the language from the manifest files at the root of the project, checking each ecosystem before package.json: a Python, PHP (Laravel) or Ruby (Rails) project that also has a front-end package.json stays Python, PHP or Ruby. bun.lock is recognized as Bun, and Java projects are also recognized by build.gradle.kts, settings.gradle(.kts), mvnw and gradlew. MAIN=./src/index.js (with the leading ./) is accepted. To force a language, set LANGUAGE=<slug> in vertracloud.config (for example LANGUAGE=deno) or send language on POST /v1/apps. In vertracloud.config.json the key is "language". The precedence is the API field, then the config file, then detection. An unknown value is refused with INVALID_LANGUAGE (HTTP 400).

Deno, .NET and Elixir

Phoenix is supported: set SECRET_KEY_BASE and PHX_HOST as environment variables. Run mix assets.deploy before zipping, or use a project without assets.

3. Databases

You choose the version when you create a database (the dashboard selector, or version in the API); the recommended one is the default. Existing databases keep their major version, and security updates (patch releases) are applied automatically whenever the database is restarted (stop, then start, in the dashboard). If you use btree_gist indexes on float columns or ltree indexes in PostgreSQL, run REINDEX on them after the restart. Existing MySQL 8.0 databases stay on 8.0, which isn’t available for new ones. Existing Redis 7.4 databases stay on 7.4. A database created from a snapshot is born on the snapshot’s major version (a MySQL 8.0 snapshot creates a MySQL 8.4 database, and a Redis 7.4 snapshot a Redis 8.2 one); version can be the same or newer, never older (SNAPSHOT_VERSION_NEWER). Restoring a snapshot taken on a newer version into an older database is refused with the same code and nothing is changed. vertracloud is the user of databases created from v26.4.8 on, and it isn’t a superuser. Older databases keep the engine’s user: postgres (database postgres), default on MongoDB (auth database admin) and Redis, and root (database default) on MySQL. The username field of the database in the API tells which one to use. PostgreSQL extensions: every trusted extension that ships with PostgreSQL can be turned on by the database’s own user with CREATE EXTENSION, with no superuser: btree_gin, btree_gist, citext, cube, dict_int, fuzzystrmatch, hstore, intarray, isn, lo, ltree, pg_trgm, pgcrypto, seg, tablefunc, tcn, tsm_system_rows, tsm_system_time, unaccent and uuid-ossp. plpgsql is already installed. Extensions that need a superuser (dblink, postgres_fdw, file_fdw, pg_stat_statements and others) and third-party ones (PostGIS, pgvector) aren’t available. ICU collations work (CREATE COLLATION ... (provider = icu, locale = 'pt-BR')). The port is unique per database — not the engine’s default — and is shown on the database page. All databases generate TLS certificates automatically (CA, server, client). Every engine requires TLS and a client certificate — a password alone never connects.

How to connect to each database

  • Host: <database-id-without-hyphens>.db.usa1.vertraweb.app. Port: unique per database (not 5432/27017/6379/3306) — copy it from the database page or GET /v1/databases/{id}.
  • Files (Certificates tab or GET /v1/databases/{id}/credentials/certificate): certificate-<id>.ca.pem = the CA that signs the server certificate (the CA file everywhere), certificate-<id>.crt = client certificate (not the CA), certificate-<id>.key = client private key, certificate-<id>.pem = bundle of client key + client certificate + the CA (MongoDB’s certificate-key file), certificate-<id>.p12 = client identity as PKCS#12 with an empty password (Prisma 6). Older databases may not offer the .ca.pem; there, the .pem works as the CA file.
  • Common errors: self-signed certificate in certificate chain = wrong or missing CA (usually the .crt passed as the CA); key values mismatch = .crt and .key are not a pair; connection requires a valid client certificate = .crt/.key not sent. Full table in Databases › Why does my database connection fail?
  • The server certificate matches the host, so full verification works.
Node.js examples for each driver (pg, mongodb, ioredis, mysql2) are on the Databases page. Minimum RAM: 1,024 MB for PostgreSQL, MongoDB and MySQL; 512 MB for Redis. Storage: 6 GB + 1 GB for every 512 MB of allocated RAM, rounded up to the GB, or 3× the RAM when that is larger (from 6 GB of RAM up) — the quota is always a whole number of GB (e.g.: 512 MB → 7 GB; 1 GB → 8 GB; 2 GB → 10 GB; 4 GB → 14 GB; 8 GB → 24 GB). No existing database loses space when the formula changes — the ceiling only grows. Operations: Start, Stop, Restart, Reset (destructive, limited to 1 per hour), Reset Password, Reset Certificates, Snapshot Download/Restore.

Data manager (the “Data” tab)

Browse and edit the database’s content from the dashboard, without exposing the password or the connection string. Dashboard only; it isn’t available through the API. SQL: schemas, tables, columns, indexes, rows and a SQL console. MongoDB: collections, indexes and documents. Redis: keys via SCAN, value by type, TTL. Minimum plan: Pro. The entire tab is unlocked from Pro up. The gate checks the plan of the database’s owner, not the plan of whoever is accessing it — a workspace member without a plan can use the Data tab on a database whose owner has the required plan, and an owner without a plan doesn’t get the feature by inviting someone on a higher plan. Below that the dashboard shows the plan card instead of the explorer. Permission: read access is granted to any workspace member; write access and the SQL console are owner or admin only (the database owner can always do it). Every mutation becomes an activity in the database log, with the operation and the object — never the content. What deliberately doesn’t exist: an administrative command or arbitrary code execution in the Redis or Mongo console. In SQL the limit is time, size and concurrency.

4. Plans and Pricing

Vertra Cloud sells 22 plans in four families: Starter (1 and 2 GB of RAM), Pro (4, 6 and 8 GB), Business (12, 16 and 24 GB) and Enterprise (14 sizes, from 32 GB to 512 GB). The number in a plan’s name is its RAM in GB. Prices are monthly, in reais; the 3, 6 and 12-month discounts are in Billing Periods and Payment. No plan has a count ceiling. The real limit is the plan’s RAM, and what refuses a creation is MEMORY_LIMIT_EXCEEDED — a 100 MB app fits 10 times in Starter 1. Minimums that consume that RAM: application 100 MB, Redis 512 MB, Postgres/MySQL/Mongo 1,024 MB. The public listing of the plans on sale, with the price, RAM, vCPU, Blob and limits of each one and the billing periods with their discounts, is GET /v1/plans (no authentication).

vCPU per Application

The vCPU column in the table above is the ceiling per application on the plan, not a total split across the account’s projects. Within that ceiling, a project’s vCPU grows with the project’s RAM:
  • Growth: on plans with up to 2 vCPU, +1 vCPU for every 512 MB of RAM; on plans with more than 2 vCPU, +1 vCPU for every 1 GB (1,024 MB).
  • Floor: a project with 512 MB or more gets at least 2 vCPU; below 512 MB, at least 1 vCPU.
  • Ceiling: never more than the plan’s vCPU. Starter 1 (1 vCPU) gives every project 1 vCPU.
The same rule applies to databases. More RAM never results in less vCPU. A change takes effect the next time the project restarts or is deployed: when your plan changes, the new ceiling reaches each application and database on its next restart or deploy, and nothing restarts by itself.

AI Agents (MCP)

Vertra doesn’t host chat or an AI model. Connect Claude, Codex, Cursor or another MCP client to the official server to operate applications, databases, files, logs and deploys with your API key. Internal validations happen during the application’s lifecycle, such as deploy, rebuild and restart; they are not a separate product surface.

Plan Names

The API identifies a plan by its name; there is no numeric plan ID. GET /v1/users/me returns plan with name, memory, expires_at and duration, or plan: null for an account without an active plan. Applications, databases and workspaces carry the owner’s plan in owner_plan (the name, or null). Plans no longer sold. economy, pro, scale, intermediary and enterprise-4 to enterprise-30 are no longer sold and are not in GET /v1/plans; asking to buy one fails with PLAN_NOT_FOR_SALE. The name enterprise-32 means Enterprise 32 (32 GB, 16 vCPU). Compare plans by name, never by position in this list. Code that read plan_id, plan.id or owner_plan_id must read plan.name and owner_plan instead (breaking change of the API, SDKs, CLI and MCP).

Features by Plan

Previous versions kept counts the versions besides the one running now. Version validity counts from the moment a version stops being the running one; the running version never expires.
Manual backups per day has two limits, counted on the calendar day in Brasília time. Per project (application or database): up to 5. Per account, across all projects: 2 per GB of plan RAM, at least 5 and at most 64, which is the value in the table: 5 on Starter 1 and 2, 8, 12 and 16 on Pro 4, 6 and 8, 24, 32 and 48 on Business 12, 16 and 24; every Enterprise size has 64. Daily backups are automatic and safety backups are taken by the platform; neither uses these limits. The 5-minute interval between manual backups of the same project still applies.
Automatic rollback and Zero-downtime deploys are deploy settings, changed in the application’s settings or with PATCH /v1/apps/{id}/config (deploy). Asking for either on a plan that doesn’t include it fails with 403 PLAN_DOES_NOT_SUPPORT_AUTO_ROLLBACK or 403 PLAN_DOES_NOT_SUPPORT_ZERO_DOWNTIME; turning them off is always allowed.
The file manager is the dashboard’s Files tab and its HTTP routes; ownership and path rules follow the same rules across the API, the CLI and the MCP.
Three different things the docs often blur together:
  • Publish (every plan) — having a site: the application gets a *.vertraweb.app subdomain and becomes reachable over the internet. The site’s HTML pages show the “Powered by Vertra Cloud” badge in the bottom-right corner on Starter 1 only; from Starter 2 up it doesn’t appear. Switching plans turns the badge on or off immediately, without a redeploy.
  • Choose the subdomain (every plan) — choosing the subdomain name, at creation or later (PATCH /v1/apps/{id}/network/subdomain).
  • Custom domains (Starter 2+) — pointing domains you own to the application, as many as the plan allows (see Custom Domains).

Account Without an Active Plan

An account without an active plan can’t create applications or databases or publish a site: those requests are refused with PLAN_REQUIRED (403). Subscribe to a plan, starting at Starter 1, to continue.
  • Projects offline for 30+ days are automatically deleted, with a snapshot taken first
  • Email notifications on days 7, 23 and 27 before deletion

Billing Periods and Payment

  • Periods: 1, 3, 6 or 12 months, on every plan. A 3-month purchase gets 10% off the monthly price, a 6-month purchase 15% off and a 12-month purchase 25% off.
  • Payment methods: PIX or credit card (Visa and Mastercard), on every plan, Starter 1 included. A coupon can restrict an order to one method.
  • Prices are always in R$. The website shows an approximate US dollar amount next to the price (≈ $) for reference only; the charge is in reais.
  • The price before you pay. POST /v1/orders/quote returns what will be charged for a plan and period, with an optional coupon: the list price, the period discount, the coupon discount, the total and whether the price is the transition price below. It creates no order and doesn’t use the coupon. The checkout shows this same value.
  • Card = automatic renewal. A card is saved to the account and the plan renews on the default card, with no loyalty period. Three days before the plan expires the card is charged and an email is sent the same day. If the charge fails it is retried on each of the next 2 days and on the expiration day; if every attempt fails, the plan expires normally. If the bank asks for confirmation, you receive an email to confirm the payment. To stop automatic renewal, use Cancel renewal on the Plan card in Billing: the plan stays active until its expiry date and the card stays saved.
  • PIX doesn’t renew. You only get the usual expiration notice and pay again when you want to.
  • Unpaid orders expire. An order that isn’t paid stays open for 72 hours after it’s created, then it expires and can no longer be paid. You get an e-mail when an order is left open, and reminders with 48 hours and 24 hours left.
  • PIX QR codes last up to 30 minutes. If a QR code expires while the order is still open, generate a new one for the same order.
  • Billing details are required to buy a plan. They are needed to issue the service invoice (NFS-e). In Brazil: name, CPF or CNPJ and the full address (CEP, street, number and neighborhood; city and state are filled from the CEP). Outside Brazil: name, street, city, country and postal code. The phone is optional. Set them under Billing; if they are missing, the checkout asks for them before payment. Through the API, an order without them fails with BILLING_DETAILS_INCOMPLETE.
  • Where payment data lives. Card number, expiry and CVV are typed into a Stripe field and tokenized in the browser; they never reach Vertra. Billing details (name, address, phone, CPF/CNPJ) are stored by Stripe, not by Vertra. Vertra keeps only the card brand, last 4 digits and expiry, to list saved cards.
  • Refunds. The first purchase of a paid plan can be refunded in full within 7 days. An automatic card renewal can also be refunded in full within 7 days of the charge; the plan goes back to its previous expiration date. Renewals you make yourself, upgrades and plan changes aren’t refundable. Requests go to [email protected].
  • Invoices (NFS-e): the Brazilian service invoice (NFS-e) is issued automatically for every paid order and delivered by email, issued in the name and CPF/CNPJ of your billing details. A full refund cancels the invoice.

Move from the Previous Plan Grid

Accounts on a plan of the previous grid were moved, at no cost, to the equivalent plan of the current grid. Economy is no longer sold: the cheapest plan is Starter 1.
  • Nothing about the time you paid changes. The expiration date stays the same, and time paid in advance stays valid. No new amount is charged before it.
  • Memory and vCPU. Each application and database keeps its memory: you choose it. The vCPU ceiling of the new plan reaches each one on its next restart or deploy (see vCPU per Application).
  • Renewing in the transition period. Until the date given in the notice e-mail, you can renew the equivalent plan for 1 month at the monthly list price of your previous plan (without any period discount or coupon you used) or at the new monthly price, whichever is lower, already with the features of the new plan. An automatic card renewal before that date follows the same rule and covers 1 month, even if your previous period was 3, 6 or 12 months; the following automatic renewals are monthly, at the current price, until you buy another period. Purchases of 3, 6 or 12 months you make yourself use the new price, with the discount of the new periods, shown at checkout. After that date, every renewal is charged at the new price.
  • Transition coupon. The accounts that received the notice e-mail also received, in it, a coupon link for the first monthly purchase of the new plan made between the transition date and the coupon’s expiry date given in the e-mail. Before the transition date the monthly renewal already uses the transition price, so the coupon isn’t accepted yet (COUPON_NOT_STARTED). The coupon is valid only for that account, only on the monthly period, for one use. See Discount Coupons.
  • Unpaid orders. An open order created before the change keeps the amount it had, and activates the equivalent plan of the new grid when paid.

5. Application Types

Display name limit for application, database and workspace: maximum 50 characters. The type is not decided once and for all at creation. An application created without web publishing can be published later (POST /v1/apps/{id}/network/publish) and unpublished again (DELETE /v1/apps/{id}/network/publish), which switches the type between 1 and 2. Two consequences users need to be told about:
  • Turning it on and off recreates the container, so the application restarts — network rules and the exposed port only exist from the moment the container is created.
  • When turning it off, the subdomain is released: there is no reservation, someone else can claim it. Every custom domain is also removed, and its CNAME and validation TXT records stay at the user’s DNS provider until the user deletes them.
Publishing requires 512 MB of RAM. RAM doesn’t increase on its own in this flow: with less than that the call returns MEMORY_BELOW_MINIMUM with details: { current_mb, minimum_mb }. Applications from a custom Docker image. On Pro, Business and Enterprise plans, an application can run from an image built from the customer’s own Dockerfile. It is created with POST /v1/apps/image, and it can be a Bot (web: false) or a Website (web: true). Rules to state exactly:
  • Build context: ZIP up to 100 MB, or a GitHub repository, with a file named exactly Dockerfile at the root. Up to 2 images per account on Pro, 6 on Business and 20 on Enterprise, counting every state. Platform linux/amd64.
  • Build: isolated build environment, time limit of 26 minutes, built image up to 10 GB uncompressed (IMAGE_TOO_LARGE). .env* files are removed from the context.
  • Runtime: always user 1000:1000. /app is the persistent disk; /tmp, /var/tmp and /run are writable but temporary; everything else is read-only. The port is the first EXPOSE or 80, injected as PORT.
  • Refused: a # syntax= other than the official frontend, RUN --security=insecure, RUN --network=host, --mount=type=ssh, --mount=type=secret. Warned: WORKDIR /app, .env* in the context, COPY/ADD of .env, VOLUME outside /app.
  • Updates: rebuild by upload, commit (on top of the current context) or push to a linked GitHub branch. A rebuild restarts apps only when the image option auto_update_apps is on (on for images created from GitHub, off for the others; change it on the image page or with PATCH /v1/images/:image_id): then every successful build restarts the image’s running apps on the new version, stopped apps stay stopped and a failing app does not block the others. Each app keeps 30 releases for 30 days, and rollback uses an already built version.
  • Snapshots hold the data of /app and the image version; restore can optionally go back to that version.
  • Deleting an image deletes every application created from it. Starter plans have no image slots and cannot create or rebuild images; a downgrade to a plan with fewer image slots than the images in use (own and image templates) is refused: delete images first.
Images and templates. An image template takes one image slot per distinct image in use, never one per application: three applications from the same template use one slot, and a new version of the template keeps the same slot. A code template uses none. The slot is released when the last application that uses the image is gone. POST /v1/apps/image refuses an image that belongs to a template (IMAGE_TEMPLATE_ONLY): the only way to run it is the template. An image that is the source of one of your private templates cannot be deleted before you remove it from the template (IMAGE_IN_USE). See Templates and stacks. Guide: Custom Docker images.

Storage

Every application gets 10 GB of storage, regardless of allocated RAM (before 09/14/2026 the ceiling was 6 GB + 2 GB for every 2 GB of RAM). Exceeding the ceiling refuses writes; lowering RAM doesn’t lower the storage ceiling, which is fixed per application.

Bandwidth Limits by Memory

Above 10 GB of RAM, the limit grows by roughly 50 Mbit per additional 256 MB.

Lifecycle

Creation is asynchronous. POST /v1/apps answers 202 Accepted right away with the application in status: "building"; install and build continue in the background with live logs. The status becomes up or down when the build ends — follow it with GET /v1/apps/{id} or the real-time log stream. While the status is building, start, stop, restart, file deploys (upload, commit), rollback, snapshots, restores and configuration changes answer 409 APP_BUILDING; deleting the application is allowed. If the build doesn’t finish, the application stays down and BUILD_INTERRUPTED appears in its log. When a build or install fails, GET /v1/apps/{id} carries the code in last_build_error (null after a successful build). If the platform cancels the creation (for example, the queue is full), the application ceases to exist and GET answers 404.

Build Command (build_command)

An optional per-application field, available on all plans. It runs on every deploy — creation, zip upload, GitHub deploy, dependency reinstall or a change to the command itself —, always after dependency installation and before the application starts. A plain restart doesn’t rerun the build if the previous build succeeded with the same command. The build runs in a build environment isolated from the application: 2× the application’s RAM (minimum 1,024 MB, maximum 4,096 MB), with a 10-minute time limit. If the command fails → the deploy fails and the application doesn’t start (BUILD_FAILED); if it times out → BUILD_TIMEOUT. On creation, a failed install or build still creates the application, stopped, with the output in its logs; the user fixes the files and starts it again. When the application has a build command, dependency installation also includes devDependencies (Tailwind, TypeScript, PostCSS and similar tools work without moving them to dependencies); without a build command, only dependencies are installed. Puppeteer uses the Chromium already available in the runtime image, so no browser is downloaded during installation. The build environment has twice the application’s bandwidth (the same bandwidth-by-RAM table, ×2) — downloading dependencies weighs more than serving traffic, and the window is short. Exceeding it interrupts the deploy, the same as happens with the application while it’s running. Common examples: The application needs to listen on port 80 — this applies to every Website-type application, with or without build_command. When creating an application from the dashboard from a zip, the platform suggests the build and start commands above based on the dependencies declared in package.json (Next.js, Nuxt, SvelteKit, Remix, React Router, Astro, Gatsby and Vite); the user can edit both before creating it. A project without a recognized framework starts from the main file, with no build command.

Configuration File (vertracloud.config)

An optional KEY=value file at the root of the uploaded zip. It only pre-fills the creation/upload form — the actual configuration lives in the dashboard (Settings tab). Recognized keys: NAME, DESCRIPTION, MAIN, MEMORY, START, BUILD, VERSION, SUBDOMAIN, LANGUAGE; any other key (including the old AUTORESTART) is ignored. VERSION pre-fills the wizard on zip upload and is used on creation from a snapshot; the version confirmed in the wizard is the one the application runs, and a version that doesn’t exist for the language is refused with INVALID_VERSION. vertracloud.config.json is also accepted, with lowercase keys. If the zip has .env.production, .env.production.local, .env.local or .env (in that order), the creation flow pre-fills the environment variables from the first non-empty one.
The file doesn’t exist on the app’s volume (since 09/14/2026). It is discarded when the zip is extracted and generated from the database with the current values when downloading the project or creating a snapshot — that’s why a downloaded zip or a restored snapshot always comes back up with the current configuration. It doesn’t appear in the file manager; a file with that name created by hand is ignored and overwritten on download.

Subdomains

  • Format: your-app.vertraweb.app
  • Unique per application
  • Web publishing (getting a subdomain) is available on every plan
  • Choosing the name of the subdomain is available on every plan, at creation or later
  • Accepted format when choosing: 3 to 50 characters, ^[a-z0-9][a-z0-9-]*[a-z0-9]$
  • Renamable later via PATCH /v1/apps/{id}/network/subdomain (minimum 5 minutes between changes)

Custom Domains

  • Available from the Starter 2 plan. Domains per application (the automatic www or root companion does not count): Starter 1: 0; Starter 2: 1; Pro: 2; Business: 5; Enterprise: 20
  • The application must be published on the web. Adding, changing or removing a domain does not restart or redeploy it
  • Configure the DNS records shown for the domain (a CNAME and, usually, a certificate validation record). Automatic DNS setup is not available yet: POST /v1/apps/{id}/domains/{domain_id}/connect always answers DOMAIN_CONNECT_UNAVAILABLE
  • SSL provisioned automatically after DNS validation
  • Automatic www: adding example.com also adds www.example.com (and the other way around). The companion redirects to the main domain with a 308, doesn’t count toward the limit, is removed with the main domain, and can be disabled with www: false. Either one can be made the main domain (primary: true)
  • Root domains need a DNS provider that supports CNAME flattening, ALIAS or ANAME; the dashboard warns about it
  • Port per domain: any port from 1 to 65535 (default 80), up to 5 different ports besides 80 per application (DOMAIN_PORT_LIMIT_REACHED beyond that)
  • Path prefix per domain (literal path such as /api, starts with /, no trailing /): added in front of every request. Recommended for APIs; a website has to run under that path
  • Redirect per domain to another domain of the same application, with status 301, 302, 307 or 308 (default 308), keeping the path and query string. A redirect can’t point to another redirect
  • Status per domain: pending (waiting for DNS), active, moved (DNS stopped pointing at Vertra Cloud) or deleted; plus the certificate state (pending, active, failed)
  • A domain that stays pending, moved or deleted for 14 days is removed automatically and the owner is notified by email
  • Unpublishing the application removes all of its domains
  • Clean up your DNS after a domain goes away (removed by you, by unpublishing, or by the 14-day cleanup): delete its CNAME and its validation TXT records at your DNS provider. Removing a domain does not touch your DNS; if the records stay, the name keeps pointing at Vertra Cloud and another customer could claim it
  • Routes: GET/POST /v1/apps/{id}/domains, PATCH/DELETE /v1/apps/{id}/domains/{domain_id}, GET /v1/apps/{id}/domains/{domain_id}/dns, POST /v1/apps/{id}/domains/{domain_id}/connect. Reads need the apps:read scope; writes need apps:write

6. Auto-Restart and Crash Loop Detection

  • Containers that crash are restarted automatically — the default on every plan, always on, with no option to turn it off
  • Exit code 0 (normal termination) does NOT trigger a restart. Neither do errors a restart can’t fix: syntax errors, missing modules, out of memory, a failed install or build, or a Vertra Shield pause
  • Every other crash is restarted, even if the application was up for only a few seconds, with a growing wait between attempts: immediately, then 10 s, 30 s, 60 s and 2 min
  • Crash loop: up to 5 automatic restarts in a 10-minute window. The 6th crash in that window suspends auto-restart for 24 hours: the application stays stopped and the dashboard says why
  • Starting or restarting the application manually clears the crash count and lifts the suspension
  • An application that stays up for 10 minutes leaves the window, so older crashes stop counting

7. Platform Protections (Vertra Shield)

Vertra Shield protects every application and database on the platform. In situations that could affect availability, Shield may pause an application temporarily; the dashboard shows the reason and a countdown.

Request rate limit per visitor IP

Applications on *.vertraweb.app and on custom domains have a limit on how many requests a single visitor IP can send. A visitor that goes over the limit is blocked temporarily, before the traffic reaches the application. Sudden traffic spikes from many different visitors do not pause the application. If your application legitimately receives high-volume traffic from a single IP, such as a server-to-server integration or webhooks, contact support.

Shield Cooldown (visible pause)

When Shield pauses an application, the dashboard shows a notice with the reason and the time remaining, and the start and restart actions become temporarily unavailable. The cooldown can grow after repeated occurrences, up to a limit of 1 hour. Shield only stops the application — it never deletes it, its files or its data. Start, restart, deploy, configuration changes and deleting the application wait until the pause ends: these actions answer 403 with the APP_SHIELD_COOLDOWN error, and details carry the reason and when the pause ends.

Regional access restrictions (*.vertraweb.app)

  • Blocked (sanctions or regulatory restrictions): Afghanistan (AF), Belarus (BY), Cuba (CU), China (CN), Iran (IR), North Korea (KP), Russia (RU), Syria (SY), Antarctica (AQ) and all Tor exit nodes (T1).
  • Browser check (security reasons): Bangladesh (BD), India (IN), Iraq (IQ), Philippines (PH), Ukraine (UA), Venezuela (VE), Vietnam (VN), Indonesia (ID) and Malaysia (MY). Only a person in a browser passes the check; API clients and webhook senders from these countries receive 403.

8. SSL/TLS

  • All sites get automatic SSL/TLS certificates
  • Automatic renewal before expiration
  • HTTP → HTTPS redirect enabled by default
  • Custom domains also get automatic SSL
  • All database connections require TLS

9. Snapshots

  • Daily backup of applications and databases, on every plan, kept for 30 days like every snapshot. It doesn’t use the manual limit below
  • Manual snapshot creation for apps and databases, on every plan — at most 1 every 5 minutes on the same project, up to 5 a day per project, and a daily limit across the whole account that depends on the plan: 2 per GB of plan RAM, at least 5 and at most 64 (5 on Starter 1 and 2, 8 to 16 on Pro, 24 to 48 on Business, 64 on Enterprise). The day is counted in Brasília time and the limits reset at midnight. Exceeding any returns SNAPSHOT_RATE_LIMITED (429) with details: { scope: "resource" | "resource_daily" | "account", retry_after }; resource is the 5-minute interval, and resource_daily and account also carry limit and resets_at
  • Restore from any earlier snapshot (30-second cooldown)
  • Download the snapshot content as a ZIP, of any size. Downloading the project directly (GET /v1/apps/{id}/download) is limited to 300 MB of files (DOWNLOAD_TOO_LARGE): above that, download a snapshot
  • Resetting a database automatically takes a safety snapshot before destroying the data, on every plan. If the backup fails, the action is blocked (SNAPSHOT_FAILED) and the database is not reset. This forced snapshot has its own ceiling, separate from the manual-creation one: at most 2 attempts per hour on the same project, and 10 per hour across all projects on the account — exceeding either returns FORCED_SNAPSHOT_RATE_LIMIT and also blocks the action.
  • Deleting an application or a database does not create a snapshot on its own. The panel’s delete dialog has the option Create snapshot before deleting, off by default (shown on plans with manual backups): when on, the panel creates the snapshot and deletes only if it succeeds; when off, it deletes right away. Through the API, DELETE on an application or database deletes right away: to keep a backup, create a snapshot first.
  • Deleting a whole stack or a custom Docker image (which deletes its apps) still takes the safety snapshot automatically, with the same ceiling. Deletions the platform makes on its own (expired plan, account without a plan) also snapshot every project first; if the snapshot fails, the resource is stopped and kept, not deleted.
  • Snapshot size limit: 4 GB (the size of the snapshot file, already compressed). Above that, creation is refused with SNAPSHOT_TOO_LARGE (413) instead of a generic error. It applies to manual, daily and safety snapshots. Downloading has no such limit: any existing snapshot can be downloaded.

10. Workspaces

Team collaboration with granular permissions and customizable roles. Available from the Starter 2 plan. The workspace owner retains control over owner-exclusive management actions.

The 20 permissions

Actions that are owner-only, regardless of any role: linking/unlinking a project, renaming/deleting the workspace, GitHub deploy webhook and turning web publishing on/off.

Seed roles

Every workspace is born with 4 roles, which are renamable and deletable (they become ordinary roles; they count toward the tier ceiling just like any other):

Custom role ceiling by plan

The 4 seed roles count toward the ceiling.

Members by plan

A pending invite counts. The progression never decreases when moving up a plan.

Invites

Two forms, both requiring members:manage:
  • Email: tied to a specific address, expires in 7 days. WORKSPACE_INVITE_EMAIL_MISMATCH if accepted by an account with a different email.
  • Link: multi-use, fixed 24h expiration, optional max_uses. Revocable at any time.
An invite can carry expires_in_days — an access expiration applied to the member who joins through it; null/absent = no expiration. A pending invite counts toward the plan’s member limit. Every account has the workspace_invites_enabled toggle (APIUser) to refuse email invites; when off, a new email invite for that account fails with WORKSPACE_INVITES_DISABLED.

Folders and favorites

Folders and favorites organize applications and databases without changing the resources. The organization is persisted on the account and has two scopes:
  • Personal: the account’s individual view, returned by GET /v1/users/me.
  • Workspace: the member’s individual view inside a workspace, returned by GET /v1/workspaces/:id. It is not shared with the other members.
A folder has a name, color, position and references to applications or databases. The allowed colors are neutral, red, orange, yellow, green, blue and purple. resource_type only accepts application and database. Favorites and folders are independent of each other. Creating, editing, deleting, moving and favoriting generates an activity. The action can come from the dashboard, a workflow or a client authenticated with an API key, such as the CLI, the MCP and the VS Code extension. The client doesn’t send origin or author_id; the API records the origin and the author automatically. The project list and Flow use the same persisted organization. In Flow, the folder and the favorite show up alongside the resources, but groups created on the canvas remain local and independent. Folders are not Flow groups. A group organizes elements on Flow’s local canvas, while a folder organizes resources within the account or workspace. There is no group_id, sync or migration between the two concepts.

Action requests

A member who lacks the permission for a sensitive action (app_delete, database_delete, snapshot_create, snapshot_restore) can open an action request; whoever has members:manage and the permission for that action approves or rejects it. It expires in 24h without a decision (WORKSPACE_ACTION_REQUEST_EXPIRED).

Retention

Deleting a workspace is a logical deletion: the name becomes available again for a new workspace; an expired plan freezes writes (invite, action, edit), never deletes data.

Route → permission

Error codes


11. File Manager

  • Available on every plan
  • Browse and edit files directly in the dashboard
  • Syntax highlighting for all languages
  • Upload via drag-and-drop (files or ZIP); the upload dialog’s Commit destination starts at the open folder and can point to any other folder, and a ZIP is merged into it
  • File tree up to 6 levels deep
  • Create, edit, move, delete files and folders
  • Lists every directory of the project, including dependency folders such as node_modules; only the file tree endpoint (GET /v1/apps/{id}/files/tree) skips dependency and build folders to stay small
  • vertracloud.config doesn’t exist on the volume: it’s generated from the database on download and on snapshot (see §Configuration File)
  • Storage: 10 GB fixed per application (see §5 Storage)

12. Deploy

Methods

  1. ZIP upload via dashboard or API
  2. GitHub integration — repository selection (including private ones)
  3. GitHub webhooks — automatic deploy on push to the chosen branch, main or master by default (every plan)
  4. VSCode extension — deploy straight from the editor

Process

Code upload → Dependency installation → Build (if there’s a build_command) → Container startup → Subdomain assignment → SSL provisioning

Upload Limits

  • Maximum ZIP size: 100 MB
  • Maximum dependency install time: 15 minutes (INSTALL_TIMEOUT if exceeded)
  • Maximum build command time: 10 minutes (BUILD_TIMEOUT if exceeded)
  • 1 exposed port per application

Automatic Rollback (Pro+)

If the new version of an application stops within 60 seconds after a deploy, the platform goes back to the previous version once and notifies the owner, with a link to the log of the failed version. Only the code files go back, not the environment variables. If the previous version also stops, the application stays stopped and the owner is notified. During those 60 seconds auto-restart does not restart the new version, so the two don’t compete. A crash after the first minute is treated as a normal crash. It is off by default, including for applications created before the feature (deploy.auto_rollback, false by default); the owner turns it on.

Zero-Downtime Deploys (Starter 2+)

From Starter 2 up, a deploy can start the new version next to the running one, wait for it to be healthy, and only then switch over, so an application that receives web traffic stays reachable during the deploy. The setting is deploy.zero_downtime, off by default:
  • off (default): stop the running version first, then start the new one.
  • auto: zero downtime only for applications that receive web traffic.
  • on: always, including applications without web traffic, such as bots. Two processes of the application run together for a while, so a bot can answer the same event twice.
Health check: the platform calls the new version over HTTP on deploy.health_check_path (/ by default). Any HTTP response counts as healthy, including 404; a refused connection, a timeout (5 seconds per attempt) or a 5xx response fails the check. The version must answer well twice in a row and still be running before traffic moves. An application that only listens on 127.0.0.1 fails the check. Known limits:
  • Both versions use the same folder, and the two processes run together for up to about 2 minutes. Data written by both at once, such as a JSON file, can be corrupted; SQLite usually copes. A database migration run at startup runs while the previous version is still serving.
  • An application that reads files on every request (PHP, static sites, a built Next.js or Nuxt app, late imports in Python) can serve a mix of old and new files while the deploy installs and builds. For those, use off.
  • If the new version does not become healthy within about 2 minutes, it is discarded and the previous version keeps running. The owner gets a notice with the log of the discarded version.
  • If the second copy can’t be started at that moment, the deploy falls back to stopping the running version first.
  • While the new version is prepared, the terminal still shows the running version, not the build. A request the old version was already answering when it was asked to stop can fail once; handling SIGTERM gracefully avoids it.

Deploy-per-Hour Limit

A per-account ceiling, renewed on a 3,600-second window. Counted: creating an application, uploading a zip, deploying via GitHub (manual or via push), reinstalling dependencies and changing the build command. Not counted: restarting and stopping. If exceeded → 429 DEPLOY_RATE_LIMITED with details.limit, details.window_seconds (3,600) and details.retry_after (seconds until it clears). A GitHub push arriving beyond the limit is ignored (it doesn’t get queued) and appears in the activity history.

Files Excluded from the ZIP

The dashboard automatically strips from the zip, before uploading: node_modules, .local, venv, .venv, __pycache__, .next, vendor, .bundle, target and .gradle.
  • JavaScript/TypeScript: node_modules, .npm, package-lock.json, .next
  • Python: venv, .venv, .cache, __pycache__, .env
  • Go: vendor
  • Rust: target
  • Java: target, .gradle
  • PHP/Ruby: vendor
dist/ and build/ (build output) are not removed automatically — if your main/start points inside them (common in projects that bundle before deploying), the platform needs these files. Only exclude them from the zip if the real entry point is outside of them.

13. Plan Downgrade

  • Available via dashboard or API: POST /v1/users/me/downgrade
  • Formula: newDays = remainingDays × (currentPrice / targetPrice)
  • Restrictions: Starter 1 cannot downgrade. A downgrade always moves to the immediately previous plan: Starter 1 → Starter 2 → Pro 4 → Pro 6 → Pro 8 → Business 12 → Business 16 → Business 24 → Enterprise 32 → … → Enterprise 512
  • Checks: Memory usage and the Docker images in use (own and image templates) must fit the target plan (INSUFFICIENT_MEMORY_FOR_DOWNGRADE, INSUFFICIENT_IMAGE_SLOTS_FOR_DOWNGRADE), rate limit (1x every 3 hours)
  • Impact: Reduced memory limit, higher-plan features disabled, workspaces lost when leaving Starter 2 for Starter 1. The saved automatic rollback and zero-downtime settings stay on the application but only act on a plan that includes them

14. VSCode Extension

  • Available on the VS Code Marketplace (search “Vertra Cloud”)
  • Features: Quick deploy, app management, file management, real-time monitoring
  • Requirements: VS Code 1.80+, active Vertra account, internet connection
  • Deploy via the command palette: Ctrl+Shift+P → “Vertra: Deploy”

15. Authentication Methods

  • Discord OAuth — Login and account linking
  • GitHub OAuth — Login and account linking
  • Google OAuth — Login and account linking
  • Magic Link — Passwordless authentication by email
  • API key — For programmatic access (managed from the dashboard settings); up to 10 keys per account, each with a name, scopes and optional allowed IPs. New format vc_live_ + 48 hex characters, shown only once; older keys (64 hex) remain valid with all scopes. Full detail in §16.

16. API Reference

Authentication

Send an API key in the Authorization header as a Bearer token:

API Keys

A new key has the vc_live_ prefix followed by 48 hexadecimal characters and is shown only once when it is created or rotated. Older keys with 64 hexadecimal characters remain valid. An account can have up to 10 keys, each with a name, access scopes and an optional list of up to 20 allowed IPs or CIDRs. An empty list allows calls from any IP address. The key needs the scope required by the endpoint. The read, write and full presets select common sets; scopes can also be chosen individually. Workspace permissions also apply to key-authenticated calls. Key management, browsing the Data tab, downgrade, new invites, deciding on action requests and querying/exporting activities all require a dashboard session. A key without the required scope gets 403 API_KEY_SCOPE_DENIED; a disallowed IP gets 403 API_KEY_IP_DENIED. An invalid key gets 401 API_KEY_INVALID. Key management is done from the dashboard.

Rate Limiting

The API applies fixed per-route limits, plus per-minute and per-day per-account limits for API key calls. The daily quota renews at midnight, Brasília time. Key-authenticated responses include x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset, x-quota-limit, x-quota-remaining and x-quota-reset. When a limit is hit, the response is 429 RATE_LIMIT_EXCEEDED or 429 DAILY_QUOTA_EXCEEDED, with details and the time to retry. The fixed per-route limits cover lifecycle operations, database reset, subdomain changes, data queries and activity export.

Response Format

  • Success: { "response": { ... } }
  • Error: { "code": "ERROR_CODE", "message": "..." }

Common Error Codes

Main Endpoints

The list below corresponds to the public API endpoints. Request and response schemas are in the API Reference. Per-route limits are detailed in API Limitations.

Applications

Databases

Users and account

Workspaces

Billing

Redeem

Services

17. Environment Variables

  • Injected into the container at startup
  • Managed via dashboard or API
  • Limits: Maximum 256 variables, key max 255 characters, value max 3,072 characters
  • Encrypted at rest
  • Forbidden keys: PORT, HOST, PATH, HOME, USER, SHELL
  • The platform sets PORT=80 and HOST=0.0.0.0 in the container

18. Automatic Cleanup (Accounts Without a Plan)

On an account without an active plan, projects that stay offline for more than 30 days are automatically deleted, with a snapshot taken first:

19. Common Problems and Fixes

Platform

  • App crashing / OOMKilled: Insufficient memory. Increase the allocated RAM. Java/Puppeteer need 512 MB+.
  • Crash loop (auto-restart disabled): 5 crashes in 10 min. Check the logs, fix the root error, redeploy.
  • App stops immediately (exit code 0): The process has nothing keeping it alive. Make sure the bot is logged in or the server is listening.
  • ZIP too large: Exclude node_modules, venv, .git, vendor, target, __pycache__.
  • Dependency installation exceeds 15 minutes (INSTALL_TIMEOUT): Remove unnecessary dependencies.
  • Build command exceeds 10 minutes (BUILD_TIMEOUT): Simplify the build_command or the framework’s build step.
  • Site times out: Bind to 0.0.0.0 (not localhost). Use the PORT value.
  • Custom domain doesn’t work: Starter 2+ plan, correct CNAME, wait for DNS propagation. A root domain needs a DNS provider with CNAME flattening. A domain with no DNS for 14 days is removed; delete its CNAME and TXT records at your DNS provider afterwards.
  • Database connection refused: TLS is required. Download certificates from the dashboard.
  • PLAN_REQUIRED: The account has no active plan. Subscribe to a plan to create applications and databases or publish a site.
  • Email not received: Check spam. Try another method (Discord, GitHub, Google).

Node.js / JavaScript

  • Cannot find module 'xxx': The package isn’t in package.json’s dependencies (it may be in devDependencies and the application has no build command, since devDependencies are installed only when there is one).
  • missing script: start: Add "start": "node index.js" under scripts in package.json.
  • Cannot use import statement outside a module: Add "type": "module" to package.json, or use require().
  • ERR_REQUIRE_ESM: The package only supports ESM. Use ESM or an older version (e.g.: node-fetch@2).
  • ERESOLVE dependency tree: Create .npmrc with legacy-peer-deps=true.
  • EADDRINUSE: Port already in use. Only one app.listen() in the code.
  • Native modules fail (better-sqlite3): Don’t upload node_modules/. The platform compiles for Linux.

TypeScript

  • tsx / ts-node: command not found: Add tsx to dependencies.
  • TSError: Unable to compile: Include tsconfig.json. Fix TS errors before deploying.

Python

  • ModuleNotFoundError: The module isn’t in requirements.txt. Generate it with pip freeze > requirements.txt.
  • requirements.txt not found: Check the exact name and extension (watch out for .txt.txt on Windows).
  • SyntaxError: invalid syntax: The selected Python version doesn’t support the feature used. Upgrade the version.
  • UnicodeDecodeError: Save files as UTF-8. Use encoding='utf-8' when opening files.
  • Flask/Django not reachable: Use Gunicorn: gunicorn app:app --bind 0.0.0.0:80.

Go

  • go.mod not found: Run go mod init and go mod tidy. Include go.mod and go.sum.
  • Incompatible binary: Compile for Linux: GOOS=linux GOARCH=amd64 go build.

Java

  • no main manifest attribute: Configure the build tool to create a fat JAR with Main-Class.
  • OutOfMemoryError: Java needs 512 MB+. Use flags: -Xmx256m -Xms128m.

Discord Bots

  • Invalid token / Improper token: Generate a new token in the Developer Portal. Set it as an environment variable.
  • DisallowedIntents: Enable privileged intents in the Developer Portal (Message Content, Server Members, Presence).
  • ClientMissingIntents: Specify intents in your code: new Client({ intents: [...] }).
  • Bot goes offline randomly: Insufficient memory or an outdated discord.js/discord.py.
  • on_message doesn’t fire (discord.py): Missing intents.message_content = True + enabling it in the Developer Portal.

Environment Variables

  • Undefined/null values: Configure via the dashboard, don’t rely on a .env file.
  • .env doesn’t work: The platform doesn’t read .env. Use the dashboard’s variables interface.

20. Discount Coupons

A coupon can be percentage-based or a fixed amount (applied once to the total, never taking the price negative), can have a validity window, redemption limit and plan or duration restrictions. An order of R$0 (a 100% coupon) is delivered instantly, without PIX. A coupon rejected at checkout returns one of the codes COUPON_INVALID, COUPON_INACTIVE, COUPON_NOT_STARTED, COUPON_EXPIRED, COUPON_EXHAUSTED, COUPON_USER_LIMIT, COUPON_AUDIENCE, COUPON_PLAN, COUPON_DURATION. A coupon that excludes the chosen payment method returns COUPON_PAYMENT_METHOD. Billing-period discounts (3 months −10%, 6 months −15%, 12 months −25%) are applied before the coupon. Transition coupons. The accounts moved from the previous plan grid that received the notice e-mail find a coupon link in it. The coupon is tied to that account, covers the first monthly purchase of the plan the account was moved to made between the transition date and the coupon’s expiry date, and can be used once. Before the transition date it is refused with COUPON_NOT_STARTED; another account, another plan or another period is refused (COUPON_AUDIENCE, COUPON_PLAN, COUPON_DURATION). See Move from the Previous Plan Grid.

21. Data Retention

What Vertra Cloud keeps about your account and projects, and for how long. The legal text is the Privacy Policy; this table is the practical summary. Your API key requests are visible to you in the dashboard, and the activity history lists lifecycle events, file manager changes, restored snapshots, domains, protection incidents, purchases, code redemptions and actions started by a connected agent.

22. Blob Storage

Blob Storage keeps files (images, videos, backups and other assets) for your projects. Public files get a public URL, private files are read through temporary links or share pages, and the same files can be reached with an S3-compatible client. It’s available on every plan. Blob usage and its quota belong to the workspace owner, so a workspace uses its owner’s quota.

Quota and limits by plan

Features that depend on the plan

Image transformations are applied through URL parameters (w, h, fit, format, q) on public images. Each distinct transformation counts once per month. Above the monthly limit the original image is served, without an error.

Limits for every plan

  • Simple upload: one request up to 100 MB per file.
  • Direct upload: up to 10 GB per object, sent in parts straight to storage, with resume and abort.
  • Path: 1 to 1,024 characters, up to 16 folders, each part up to 255 characters of letters, digits, ., _, - and space. Every file needs an extension.
  • Blocked types: exe, msi, dll, bat, cmd, com, scr, cpl, pif, hta, vbs, vbe, jse, wsf, wsh, msc, reg, lnk, sys, drv, ps1, apk, xpi, dmg, pkg, deb, rpm and appimage. Executables are also detected from their content.
  • HTML, HTM, SVG, XML and XHTML are always downloaded (attachment), never shown inside the page.
  • Folder rules set defaults for a folder: visibility, expiration, maximum size, extensions, Cache-Control, and deletion after a number of days (1 to 3,650).

Quota, downgrades and grace period

  • A write that would take the account past its quota is blocked with STORAGE_QUOTA_EXCEEDED. Deleting objects frees space.
  • After a downgrade, your files stay. If they use more than the new quota, new uploads are blocked and the excess is charged at R$0.20 per started GB on the next renewal.
  • When the plan expires, the account enters a 72-hour grace period: uploads and other writes are blocked (BLOB_NAMESPACE_SUSPENDED), while public files, temporary links and share pages keep working. Renewing within those 72 hours reactivates everything. A suspension for abuse is different: the files go offline right away.
  • After the grace period the files are deleted. Deletion is permanent and there is no backup.
  • Share pages open on the website. A share can have an expiration, a download limit and, from Business, a password. A share doesn’t protect a public object.

Access

  • API key scopes: blob:read and blob:write. Workspace permissions: blob:read and blob:manage. A key with only blob:* scopes can be restricted to one folder (blob_prefix, a path ending in /): it reaches only objects, uploads and shares inside that folder, and the S3 credentials derived from it inherit the limit. Everything else (rules, domains, statistics) answers 403 BLOB_PREFIX_DENIED. A stack creates its own restricted key for its Blob folder.
  • Upload token: a short-lived vcup_ token for browsers. It fixes the path or folder, size, extensions and visibility, and it is accepted only on the upload routes.
  • S3-compatible access: GET /v1/s3/credentials, limited to 10 calls per hour. Revoking the API key that created it cuts S3 access at once. S3 requests per 10 seconds per account: Starter 1 100, Starter 2 200, Pro 400, Business and Enterprise 600; CopyObject/UploadPartCopy 20. Above it: 503 SlowDown.
  • Every route is documented on its own page under API Reference > Blob Storage.

Blob error codes


23. Templates and stacks

A template is a recipe that creates ready-made software (or a starter project) in one operation, after a few questions. A stack is the group of applications, databases and Blob folders that belong together; a template with more than one service creates one. Guides: Templates.

Origins

Users do not publish to the public catalog from the dashboard. Private templates are created on plans that can create Docker images (Pro and above), up to 5 per account; in a workspace only the owner creates and manages them. A member of a workspace does not deploy templates in it: only the owner does (WORKSPACE_OWNER_ONLY).

Code and image templates

In both, /app is the persistent disk and the rest of the filesystem is read-only; the user is 1000:1000.

Required plan

The plan needed is calculated from the template and your answers, and can be raised by a min_plan the author declares: free memory for the sum of the minimum memory of every service, web publishing when a service needs it, a plan with Docker images and a free slot for an image template, a plan with Blob when the template has a Blob folder, and the account limits of applications, databases and deploys per hour. When it does not fit, the API answers TEMPLATE_PLAN_REQUIRED, TEMPLATE_RAM_INSUFFICIENT or TEMPLATE_IMAGE_SLOTS_EXHAUSTED and the dashboard explains the reason.

Using a template

  • POST /v1/templates/{id}/deploy starts a job (202) that validates the plan and memory, creates databases and waits for them, creates the applications, writes the variables, starts everything and waits for the health check. Any failure undoes everything the job created (TEMPLATE_DEPLOY_FAILED, details.step); before deleting an application or a database the undo takes a safety snapshot. A template of a workspace deploys only inside that workspace, and only by its owner. Send an Idempotency-Key to make it safe to repeat. Only one deploy of the same template runs at a time (TEMPLATE_DEPLOY_IN_PROGRESS).
  • Questions have types (text, secret, select, boolean, number, url, email); secrets can be generated for you. Outputs, such as the address or a generated token, are shown at the end; a secret output is shown once.
  • Updates are never automatic: a new version shows New version available; updating takes a safety snapshot first, updates the services one by one and, if any fails, restores all of them. Permissions never become stricter in an existing application. Some templates lock the blue-green deploy (two copies running at once would break them) and require rollback to restore the data too (TEMPLATE_ROLLBACK_REQUIRES_DATA).
  • Changing the code of an application from an open template detaches it from the template; the API asks for confirm_detach first (TEMPLATE_DETACH_CONFIRMATION_REQUIRED).

Stacks

  • Applications in a stack reach databases through the public address, with client certificates managed by the platform and left out of downloads, snapshots and “save as template”. A network mode other than public returns STACK_NETWORK_MODE_UNAVAILABLE.
  • A connection links an application to a database, a Blob folder or another application. Its variables are managed: updated when the database password, certificate or host changes, and marked in the dashboard. Editing one by hand turns it into a normal variable. The API returns restart_required and apps_to_restart when applications must restart.
  • A resource belongs to at most one stack, in the same account or workspace as the stack. A stack can be created from a template, from the /new flow, by adding a database to an application, or by transforming a folder that holds an application and a database.
  • Leaving a stack: keep the connection (variables become normal ones with the public address) or remove them. Deleting a stack asks whether to only undo it (the resources stay) or delete everything; for the Blob folder the choice is keep (default) or delete.
  • Folders organize, stacks connect: a stack has its own card in the list and its own box with the connections in the Flow, and it can live inside a folder.
Revoking a link stops new uses and offers no more updates; what was created keeps running, and deleting the original image, template or account does not affect the copies. The preview shows the author, what will be created and Not verified by Vertra. Links can have max_uses and an expiry (SHARE_LINK_REVOKED, SHARE_LINK_EXPIRED, SHARE_LINK_EXHAUSTED). Use Report on a preview or on a created application; for serious cases with evidence, write to [email protected]. An application that came from a use link cannot be saved as a template or shared (TEMPLATE_SAVE_NOT_ALLOWED).

Closed templates

A private template can be closed: whoever uses it can start, stop, restart, read logs and metrics, create snapshots and restore them (only into an application of the same template), roll back, answer the questions, change memory when allowed and update. They cannot use the file manager, download the application or its snapshots, create free variables, edit commands, open a terminal, deploy code, save it as a template or detach it. The API enforces this on every route (TEMPLATE_CLOSED); a snapshot of a closed application can never be downloaded. Databases are never closed. Variables the author hides (env_hidden, only allowed in a closed template) are injected and never returned (MANAGED_ENV_READONLY); unlinking the application or stack from the template deletes the hidden values.

Manifest limits

vertracloud.template.yaml: up to 40 questions, 16 outputs, 8 services and 10 tags. Name up to 50 characters, description up to 128. The public JSON Schema is /templates/schema/v1.json. Placeholders are resolved on the server; an unknown placeholder or service makes the manifest invalid (TEMPLATE_MANIFEST_INVALID).

Template and stack error codes


24. Glossary

Last modified on November 8, 2026