Free trial · 14 days · Cancel anytime

Projects

Group containers, volumes and networks into an isolated unit.

What is a project?

A project bundles an application's containers (front, back, database, companion services) inside its own isolated networks, with its dedicated quota. It belongs to a single organization.

A project's detail page is split into nine tabs: Containers, Topology, Deployment, Repositories, Logs, Secrets, Backups, Security and Network groups. Each is described below, except Security, which has a page of its own: it also holds the vulnerability history chart.

The vulnerability history, on the Security tab.

The interface's foldable cards (topology resource sections, backups, security, deployment mode, secrets) remember their open or closed state in your browser: you find them as you left them on your next visit.

The project list

The Projects page lists every project in the active organization. Each row carries an icon, the name and short identifier, the number of containers used (against the quota), the creation date and actions (open, rename, delete). A counter at the top shows your usage against your plan.

Each row also shows a 7-day health bar, the same visual language as the dashboard: one colored cell per day of the current week, summarizing the worst container state seen that day. Hover a cell to see the date, the status and the healthy versus total container count. Click a cell to open that day's detailed history, scoped to this project.

The organization's project list, with the quota counter.

A self-hosted project carries, next to its name, a tag with the name of the server that hosts it: you can see at a glance where each project runs, without opening its page.

Self-hosted projects carry the name of their server.

Create a project

Click Create a project: a dialog offers three ways to get started. Choose Frameworks to deploy an app from its repository, then give the project a name, the short identifier is derived from it and locked at creation. Depending on your deployment target, pick the agent (server) that will host the project. Confirm to create the empty project, ready to receive containers.

The number of projects and containers per project depends on your plan. When the project quota is reached, the Create a project dialog still opens, but its three options are disabled: it shows your usage and offers to upgrade to a higher plan.

To take over an app that already runs on another host, choose Migrate from another host in that same dialog: the assistant creates the project for you, in beta. See Migrate to Pierrr.

Creating a container

Inside a project, the dedicated button opens container creation. You give a name, from which the short identifier is derived, then:

  • The type. A web container is reachable over HTTP and gets a subdomain; a worker container, for a message queue or a scheduled job, exposes no port and is not probed over HTTP. Pick it at creation, it cannot be changed afterwards.
  • The repository at your code provider, the branch to watch and the folder holding the code, with a browser to find it.
  • The framework. If your Dockerfile carries a LABEL framework line, Pierrr picks it up on its own and locks the choice; otherwise you select it, for information only when a Dockerfile exists, or to choose the template Pierrr will generate when there is none.
  • A "Monorepo: build from the repository root" switch, for when the Dockerfile lives in a subfolder but the build needs to see the whole repository.

Containers tab

The Containers tab shows each container of the project as a card: detected framework logo, name and short identifier, and live state. You can:

  • Read each container's health, Healthy, Online, Degraded or Offline, with a colored badge. A vulnerability badge per severity appears when a scan exists; a click opens the Security tab.
  • Start a stopped container, individually or all at once via Start all.
  • Stop one or all containers to pause traffic.
  • Restart a container, useful after changing environment variables or rotating a secret.
  • Deploy a container with the lightning button on its card: one click builds and deploys the latest version of the watched branch, and the chevron next to it lets you pick a different commit.
  • Create a container via the dedicated button (blocked by an upgrade dialog when the project quota is reached).
  • Adjust a container's instance count with the +/- stepper next to "Instance count", directly on the card.
Each container shows its framework logo, live state and actions.

Project-level actions

The project header has a Project actions menu with Start all, Stop all and Restart all, plus the Create button. An action that has nothing to do is greyed out with the reason. The deployment mode (the targeted agent and how deployments are triggered) is set in the Deployment tab.

A container's More actions menu

The More actions menu on each card gives access to View Dockerfile (the linked repository's, or Pierrr's template, with a copy button), Resources, Rename and Delete. Renaming recreates the container: there is a brief interruption and its Pierrr subdomain changes, but your custom domains and data are kept. Deleting asks you to type the container name and cannot be undone.

The More actions menu: Dockerfile, Resources, Extended capabilities, Enhanced isolation, Rename and Delete.

Locked actions and sleeping projects

An action your role does not allow is never hidden: it stays visible, greyed out, with a tooltip giving the reason. This covers start and stop, resources, secrets, data import, backups, scans and container creation.

On the Free plan, projects sleep from 00:00 to 08:00 (UTC) and are stopped after 30 days without activity. A banner at the top of the project says so, with a Wake up button, and the start actions are greyed out with the reason, for example "Free plan: project asleep until 08:00 (UTC)".

Scaling instance count

Every container card shows an "Instance count" stepper that lets you raise or lower the container's instance count without going through a new deployment. Until the number of running instances catches up with the target, a hint like "2/3 active" stays visible on the card.

The maximum instance count per container depends on your plan: 1 on Free, 2 on Starter, 4 on Pro and 8 on Business, with an absolute ceiling of 50 instances regardless of plan. Past the plan's quota, the stepper opens an upgrade dialog instead of increasing the instance count. An extra instance is also refused when the project's resource budget is used up.

Processor and memory limits

Each container exposes its resource limits. Two values, not to be confused: the ceiling, which the container cannot exceed, and the guaranteed share, which stays reserved for it even when a greedy neighbor runs on the same machine. The second protects your service, the first protects the others. Set them from the Resources button in the card's More actions menu, in the Resource limits dialog: CPU limit in millicores (1000 = 1 vCPU), Memory limit in MB, Guaranteed CPU and Guaranteed memory (empty, nothing is reserved). Everyone can read these values, editing is locked with its reason for a role without the right.

The Resource limits dialog: CPU and memory ceilings and guaranteed shares.

What you can ask for a container depends on what is left in the resource budget of its project, described below.

Enhanced isolation

Each container's menu offers enhanced isolation: the application then runs in an execution environment isolated from the server, which adds a barrier between its code and the machine. It is useful for code you do not fully control, a third-party dependency or a tool you did not write. Switching the option on or off recreates the container.

The Enhanced isolation dialog: the option, its measured overhead and the container recreation it triggers.

It has a cost, measured on our test servers: about 50 to 65 MB of extra memory per container, counted in the application's limit, a start two to four times slower, and a response capacity divided by 2 to 5 depending on the application. Check that the application's memory limit leaves that margin before switching it on.

It is reserved for web applications: a database would lose most of its performance, and the option stays locked for it. On your own server, it shows locked, with the reason, until the server offers that environment or while its agent needs an update. The option stays visible to every role; only those who can change the project can switch it.

A project's resource budget

A plan does not set a ceiling per app but a budget per project, which you split freely between the project's apps: 1 vCPU and 1 GB of memory on Free, 2 vCPU and 2 GB on Starter, 3 vCPU and 4 GB on Pro, 4 vCPU and 8 GB on Business.

The budget is counted by adding up, for each app, its limit multiplied by its number of instances. A stopped app still counts as one instance, so restarting it never depends on the budget. Databases and managed services do not count.

The project's Resource budget card shows two gauges, CPU and memory committed, and the split by app. Creating an app, raising a limit or adding an instance that would go over the budget is refused before it has any effect; lowering a limit always goes through. The Split evenly button gives every instance the same share of 90% of the budget: the remaining 10% is never distributed, kept as headroom, after a preview you confirm.

Projects that run on your own server, with Pierrr Agent, follow the same budget.

If a project ends up over its budget, nothing is stopped right away: you have 7 days, announced by an email and reminded the day before. After that, only the surplus apps are stopped, the oldest keep running, and nothing is deleted.

  • Only if the project goes over its budget
A plan sets a budget per project, which you split between its apps: each app's limit multiplied by its number of instances. Databases and managed services do not count. Going over does not stop anything right away: you have 7 days, then only the surplus apps are stopped.

The budget does not only limit what you reserve: Pierrr also watches what a project's apps actually use, measured every minute.

  • As soon as the project's apps use more than its budget, a banner with a countdown appears at the top of every dashboard page, and an alert badge flags the project in the sidebar, in the project list, on its Topology tab and on the Resource budget card. After 10 minutes in a row over the budget, the owners and maintainers get an email and a notification, and the countdown switches to the hour left before the stop.
  • If usage is still over the budget when that hour is up, the project's apps are stopped. Databases, caches and stored files keep running, nothing is deleted, and visitors of the site see a "temporarily unavailable" page.
  • Coming back under the budget at any point before the stop cancels everything.
  • A stopped project can be restarted from the Resource budget card, once per rolling 24 hours. If it goes over again within those 24 hours, bringing it back goes through support, with the response time of your plan. The restart is not counted if you have lowered the project's limits, or upgraded your plan, so that they fit the budget.

Projects that run on your own server are not concerned by this watch. In practice it only triggers when a project's reserved limits are above its budget, for example when a plan or budget change leaves it above, until it gets back within: as long as they fit the budget, no app can use more than its own limit.

The health path, /health by default

Pierrr enforces its own healthcheck on every container: a GET request to http://127.0.0.1:<PORT>/health by default, regardless of any HEALTHCHECK declared in your Dockerfile (which is ignored: Pierrr's compose-level healthcheck always takes precedence). The path can be set per application, see the build settings in the Repositories tab. Your app must therefore respond 200 on that path, without your framework's global prefix if it has one (e.g. NestJS with setGlobalPrefix('api'): explicitly exclude the health route from the prefix). Without this, the container will never turn healthy and every deployment will fail after 90 seconds, even if the app starts and runs fine.

Answering 200 is not enough to make the signal useful. Answer 200 only once your service can actually serve: database reachable AND schema in place, required dependencies ready. A route that returns 200 as soon as the process is up reports a live process, not a working service, and can never surface a failure. A real case: a `/health` checking a Postgres extension that Pierrr installs before any customer code answered 200 while the database had no tables at all.

typescript
// NestJS: exclude /health from the global prefix
app.setGlobalPrefix('api/v1', { exclude: ['health'] });
  • No configuration on Pierrr's side: the /health route just needs to exist and return 200 on your app's port. A HEALTHCHECK in your Dockerfile isn't required (it's ignored), no need to maintain one in parallel.
  • During a deployment, Pierrr waits for the new instance to turn healthy on /health (up to 90 seconds) before switching traffic over; if it doesn't, the old instance stays in place and the deployment is rolled back.
  • The healthy/unhealthy state reported by Docker feeds the server's aggregate health count and the unhealthy-container notification, on top of the plain run state.

/health must stay reachable without authentication and without a prefix, on the same port as the rest of your app.

Predefined Dockerfiles and preflight

Pierrr ships a predefined Dockerfile for 34 frameworks: Next.js, NestJS, Express, Fastify, Bun, React, Vue, Svelte, Astro, Remix, Nuxt, Node, BullMQ, static sites, FastAPI, Flask, Django, Python, Go, Rust, Deno, Ruby, Rails, PHP, Laravel, Spring, WordPress, PostgreSQL, MySQL, MongoDB, Redis, RabbitMQ, Kafka and Traefik. In the common case you have nothing to write: you pick the framework when you create the application, and Pierrr knows how to build it, start it and watch it. Each of these templates is proven against a sample application that builds, starts and answers.

When your repository contains a Dockerfile, that one wins: Pierrr uses it as is and never replaces it with a template. Before each build, Pierrr checks your Dockerfile and a few project files to catch the most common mistakes. A blocking error stops the build with a clear message ; warnings do not stop the build and show up in the deployment logs, prefixed with preflight.

With a predefined Dockerfile, the install, build and start commands stay yours: you change them in the application settings, along with the output folder and the health path. Your values replace the template's, with no line of Dockerfile to write.

Vault variables are injected at container RUNTIME, never as build arguments: `docker build` does not see them. This matters for frameworks that inline their public variables at compile time (`NEXT_PUBLIC_*` with Next.js, `VITE_*` with Vite): the vault value will not reach your bundle, which keeps the fallback hard-coded in your source. Nothing fails at deploy, and the defect only shows up in the browser's network calls. Read those values server-side at startup and pass them to your pages instead of freezing them at build time.

  • LABEL before the first FROM (blocking) : Docker refuses to build a Dockerfile whose LABEL line comes before the first FROM. Always place your LABEL framework line after a FROM. This case is also flagged in red as soon as you pick the repo, before the build even starts.
  • Prisma without openssl or schema (warning) : the prisma generate postinstall needs the schema and openssl at build time. On an alpine base, add RUN apk add --no-cache openssl and copy COPY prisma ./prisma before installing dependencies.
  • reactCompiler without @swc/helpers (warning) : if reactCompiler is enabled in next.config, add @swc/helpers to your dependencies, otherwise Next's standalone build won't bundle it and the container crashes at startup.
  • Application with no web server (blocking): a queue-processing template listens on no port, so it has no health answer to give. Declare the application as a "Worker" in its settings. Otherwise the deployment is refused right away, naming the setting to change, instead of waiting a long time for an answer that will never come.

These checks save you from a build that fails with no message or a container that crashes at startup. Warnings stay advisory : you keep full control over your Dockerfile.

Values your framework needs at build time

Some frameworks resolve values while compiling rather than at runtime. The common case is Next.js' `metadataBase`, which canonical URLs and Open Graph tags depend on: if your pages are statically prerendered, the value is frozen during `docker build`, before Pierrr injects anything. Pages then ship with `http://localhost:3000` as their canonical, which is the URL a crawler or a link preview will actually use.

Pierrr does not pass vault variables as build arguments, and will not: an `ARG` stays readable in the image history (`docker history`), so the mechanism quietly invites values that have no business being there. A declared but unset `ARG` also yields an empty string rather than an absent variable, which turns an oversight into `new URL('')` instead of a clear error.

The fix is two rules. First, read the variable lazily inside a function, never in a module-level `const`: a `const` is evaluated once and can be frozen into the bundle, whereas a function call re-reads `process.env` in the running container.

typescript
// env.ts: read on every call, server-side, in the running container.
export function getPublicOrigin(): URL {
  return new URL(process.env.PUBLIC_WEB_ORIGIN ?? 'http://localhost:3000');
}

// app/layout.tsx
export async function generateMetadata(): Promise<Metadata> {
  return { metadataBase: getPublicOrigin() };
}

Second, make sure the metadata is really evaluated per request. A statically prerendered page runs `generateMetadata` at build time, where the variable does not exist yet. Incremental regeneration does not quite close it either: the first visit after a deploy still gets the build output. To be correct from the very first request, derive the origin from the `Host` header instead of a variable, which makes the page dynamic and removes the need to configure anything:

typescript
import { headers } from 'next/headers';

export async function generateMetadata(): Promise<Metadata> {
  const h = await headers();
  const host = h.get('x-forwarded-host') ?? h.get('host');
  return { metadataBase: new URL(`https://${host}`) };
}

For a value a client component needs, the principle is the same: read it in a server component and pass it down as a prop. The client component receives it at runtime, never depending on a value frozen at compile time.

Topology tab

The Topology shows containers, the managed database, object storage and, once linked, your Redis add-on as a hierarchical graph organized by tier: proxy, frontend, backend, cache, database. Networks are not separate nodes: they appear as edges between the containers that share them, click an edge to see the underlying network links (name, aliases). Select a node to see its details (mount points, attached networks, aliases, isolation, vulnerability badge) and act on it: start, stop or restart a container, mount or unmount a volume, unlink a Redis instance (the managed Postgres is now unlinked from the Secrets tab, see below). The object storage node shows its bucket and credentials, with a Link storage link while it is not connected. The database node is labeled shared (Free plan) or dedicated (paid plan), a distinction driven by the plan today, not yet a physically separate instance. When the project has no container yet, an empty state invites you to create one.

  • Shared network
  • Healthy container
  • Degraded container
  • Resource connected to the container
Each container is a node, with its health. The solid line between two containers is a network they share. A database, cache, storage or volume only reaches a container if you connect it: that is the dashed line.
The project's containers, volumes and networks as an interactive graph.

Graph options

The Refresh button rereads node health. The Display options menu hides volumes, networks or containers, and the minimap can be shown or hidden. Save position keeps the layout you gave the nodes, Reset position returns to the automatic layout. The legend recalls the four states Healthy, Online, Degraded and Offline.

Project consumption

Below the graph, two foldable cards track the load. Project resources shows CPU and memory gauges against the sum of allocated resources, and last-hour curves with Current, Average and Peak values. Per-application detail gives the same reading app by app, with the current split. Folded, they keep a preview: the current load, or the number of applications and the hungriest one. Without measurements, an empty state says "No measurements yet".

The project Resources card: CPU and memory gauges, and curves over the last hour.
The Per application card: CPU and memory curves and the current split between containers.
Folded, the cards keep a preview of the current load.

Volume and Redis add-ons

A volume and a Redis instance are purchased as add-ons from Billing, then Add-ons. A volume is bought by its size in GB (0.50 € per GB per month), with no name and no project: it then shows up on your project pages, where you link it to a project and give it its name. That link is permanent, a volume cannot be moved to another project. A linked volume starts unmounted, open its node in Topology to attach it to a container and a mount path. A purchased Redis instance has no node until it's linked : link it to a project from the Redis panel above Topology, then unlink it from its node in Topology once connected.

Connecting a resource to a container

A managed resource only reaches a container if you connect it there explicitly: buying object storage or linking a database is not enough, and nothing is broadcast to the whole project by default. The control lives on the RESOURCE node, not on the container's: open the database, cache or storage node in the Topology and tick the containers that should receive it. The matching variables (DATABASE_*, CACHE_*, STORAGE_*) are injected on the next deploy, and a container left unconnected will start without them. On a container, the Connected resources line shows what it is attached to, or says plainly that it is attached to nothing.

Deployments tab

The history lists every deployment of the project, newest first (paginated). For each row:

  • The four pipeline stages, build (image build), push (push to the registry), load (image pull) and start (container create + launch), each with its status icon.
  • A mono status badge (Succeeded, Failed, Running, Pending, Canceled).
  • The container (logo + name + short identifier), the branch and commit (clickable through to your repository at your code provider, your own instance included), the start time and the duration.
  • The number of attempts when a deployment was retried. A stage can also be Skipped or Reused. A deployment whose container was deleted shows Deleted, noting that details are unavailable.
  • No answer: the deployment is cancelled and the previous version stays
  • Deployment succeeded
A deployment always goes through the same steps: building the image, publishing, loading, starting. Traffic only switches once the new version is healthy. On failure, the previous version stays online, and a retry restarts from the failed step.
The deployment history: container, status, 4 stages, commit, branch, duration.

Deploying a specific commit

The deploy button on each container card is split in two. The main action has not changed: one click builds and deploys the latest version of the watched branch, with no extra step and no extra request.

The chevron next to it opens a dialog listing the recent commits of that branch. A search box filters the list by message, by author or by hash, in its short form as well as its full one. Each row shows the commit message, its short hash and its author.

Selecting a row immediately deploys that version instead of the latest one. This is how you roll back to a known-good version without touching your repository, or ship a fix you already validated while the branch has moved on.

The chosen commit is recorded on the deployment: its hash is what you find in the history, and that is the code actually put online. If the container is already running and the last image built matches the commit you picked, that image is reused rather than rebuilt.

The list is fetched when the dialog opens, never before, and covers the last thirty commits of the watched branch. If it cannot be retrieved, for instance when no repository is linked to the container, the dialog says so and deploying the latest version stays available.

Where the project runs

The Deployment tab carries the project's deployment mode, in an accordion open by default whose state is remembered: on Pierrr's infrastructure, the default, or self-hosted on one of your servers, which you then pick from the list of online agents.

On your server, the project's database is created on the machine by the agent, never at Pierrr: the console shows it as "Database on your server", with no quota gauge. The cache and volumes are added from the project, with nothing to buy. Such a project cannot change servers while it has its database, except to an agent reinstalled on the same machine. The database panel and import go through the agent, on the server itself. Its security scans run on the server itself, and only their report comes back to Pierrr. A WordPress site is created straight on your server, with its database on the machine; a site created on Pierrr hosting keeps its database at Pierrr and cannot move there.

On your server, the project's database is created on the machine; cache and volumes are added without purchase.

On a server of your own, an app's own URL shows a failure badge when its public access could not be synced, for example because ports 80 and 443 are held by another program: the plain link gave no hint that the address was not reachable.

The failure badge appears next to the application's own URL.

Move the database to your server

A project whose database is managed by Pierrr can move to one of your servers with its data. In the Deployment tab, choose "Your server" and then the target server: the "Migrate the database to this server" button replaces the plain save of the mode. It needs a server on agent 0.78.0-beta or later, with its setup finished and a healthy local database; otherwise the button stays locked, with the reason.

With "Your server" and a target server, the migration button replaces the plain save.

A dialog sums up what will happen before the copy starts:

  • The project is stopped during the copy, from a few seconds to a few minutes depending on the database size.
  • Your apps are not redeployed automatically: once the migration is done, redeploy them on the new server.
  • The original database is kept for 7 days, then deleted automatically. You can delete it sooner.
  • If the copy fails, nothing changes: the project stays on Pierrr with its database and restarts.
  • A step that can fail without changing anything
  • A step you do yourself
The project is stopped during the copy, from a few seconds to a few minutes depending on the database size. If the copy fails, nothing changes: the project stays on Pierrr with its database and restarts. After the switch, the original database is kept for 7 days and then deleted, and you still need to redeploy your applications on the new server.
What will happen, before the copy starts.

The "Database migration in progress" card follows the steps: project stop, database export, import on the server, project switch, cleanup. It carries on even if you leave the page, and the mode selection is locked during the copy.

The project stays stopped until the copy is done.

When it ends, the "Database migrated to your server" card reminds you to redeploy your apps and shows the date the original database will be deleted. "Delete now" deletes it sooner, after you type the project identifier: it is final. On failure, the "The database migration failed" card gives the reason and confirms the project is untouched on Pierrr.

The original database stays available for 7 days, or until it is deleted.
On failure, the project stays on Pierrr with its original database.

Fast mode

By default, stacking several pushes makes each deployment wait behind the previous one: the version you actually wanted online goes last, after builds that became pointless. The Fast mode switch, in the Deployment mode accordion, changes that: a new deployment cancels the one still running for that container and takes its place.

The effect stays bound to the container involved: redeploying your api container never interrupts the build running for your web container. The setting is off by default, and a deployment dropped this way carries its own reason in the history, not to be confused with a manual cancellation.

Cancel, re-run, export

  • Cancel, while the deployment is pending or running.
  • Re-run, on a failed deployment. The new attempt restarts from the stage that failed, and the stages that already succeeded are reused as they are.
  • Download an attempt's full log, to read it outside the console or attach it to a support request.

Build logs are kept for a length of time that depends on your plan, recalled on the deployment detail page.

Deployment detail

Click a row to open the detail. The header shows the status and the actions: Cancel, Retry, Redeploy this commit, Open site, Attempts and More actions. The Details card sums up the status, the failing stage, the origin (automatic or started by a member), the duration, the current container, the intent (Build only or Build and go live), the domains, the source and the commit fingerprint, which can be copied. Shortcuts lead to runtime logs, health history, resources, security and the repository.

A deployment's detail: stages, attempts and full logs.

Understanding a failure

A failed deployment states its reason in plain words (cancelled, replaced by a newer one, project asleep, silent server, not enough memory, health check not met on the path, build, publish, load or start failure), with help actions: Adjust resources, Runtime logs, Set the health path.

The rest of the page is organized in foldable sections: Build settings (with recommendations), Build logs (open by default, stage filter, search and Download, retention depending on the plan), Deployment summary (stage, status, duration, start and end per attempt), Checks (the health check at startup) and Custom domains. This is where you diagnose a broken build or a container that won't start.

A failed deployment: plain-words reason, help buttons, summary and checks.

Repositories tab

The Repositories tab lists the code provider repositories linked to the project's containers, with the watched branch and the Dockerfile path. It is the link between your code and each container deployed from a push. Each row's menu lets you Edit the branch, Edit build paths (Dockerfile, .env template, monorepo) and Edit build commands.

Each linked repository shows an Auto-deploy switch, on by default. While it stays on, a push on the watched branch triggers a build and a deployment for that container, whatever your code provider. Turning it off only affects that container: other containers on the same project or repository keep deploying normally, and the link with your code provider stays in place, it simply does nothing while the switch is off.

Click Edit next to the watched branch to point the container at a different one, for example to promote a feature branch to production, without deleting and recreating it. This only changes which future pushes trigger a deploy: it does not redeploy the container that is currently running. Trigger a deploy afterward with a push on the new branch, or with the container's Build button.

Build settings are edited per application: install, build and start commands, output folder and health path.

Build settings: install, build, start, output folder and health path.
The code provider repositories linked to the project's containers.

Logs tab

The Logs tab shows container logs. Pick the container in the selector; when the project has no container, an empty state says so.

By default, logs refresh periodically, with a Refresh button. Toggle Live to stream them in real time instead; the two modes never run at the same time. The bar also offers a time window selector, search with a line counter (x / y) and the All containers option. Log colors are preserved.

For a project hosted on your server, the logs are read on the machine, from the agent, when you open the tab: they are never stored at Pierrr.

Live logs, with a container selector.

Secrets tab

The Secrets tab manages a per-project encrypted vault (environment variables, API keys…). Each secret can be injected into containers in one of three modes:

  • Bound, a container only receives the secrets you explicitly bound to it.
  • Detected, a container receives the vault secrets whose key is declared in its `.env.example`.
  • All, a container receives EVERY vault secret (org + project scope), regardless of binding or `.env.example`.

Choosing the mode is reserved for paid plans. On the Free plan it is fixed: every container receives the whole vault.

  • Injected by Pierrr, stronger than the vault
Your variables go into the project's encrypted vault, masked by default. Depending on the chosen mode, each container receives the ones bound to it, the ones its template declares, or all of them. They reach it at start-up, never during the build. Variables managed by Pierrr always take precedence over the vault.
The configuration managed by Pierrr: automatically injected variables and managed Postgres.

"Managed by Pierrr" section

A read-only section lists the variables Pierrr injects into your containers automatically, no manual setup needed. They always win over the vault and can't be edited or deleted from it while the matching resource stays linked. If you try to manually create a secret with one of these names, Pierrr rejects it and spells out exactly how to unlock the variable: unlink the corresponding resource.

  • Managed Postgres (linked by default to every project): `DATABASE_URL`, `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD`. These names are deliberately generic rather than `PG*`: libpq does not auto-recognize them, so a container hosting its own Postgres never has its tooling accidentally redirected at the managed instance. A "Use Pierrr's managed Postgres" toggle, right above this section in the Secrets tab, lets you unlink the project without destroying the database or its credentials. Once unlinked, these names become free and you can set them yourself in the vault, for example to point `DATABASE_URL` at an external database. Unlinking is a paid-plan feature: on the Free plan, the toggle opens an upgrade dialog.
  • Redis add-on (once linked to this project from Billing, then Add-ons): `CACHE_URL`, `CACHE_HOST`, `CACHE_PORT`, `CACHE_PASSWORD`. Same reasoning as above: `CACHE_*` rather than `REDIS_*`, so redis-cli does not latch onto them by itself. Unlink it from its node in the Topology tab to free these names; no plan restriction applies.
  • Object storage add-on, S3-compatible (once linked to the project, then each container connected individually): `STORAGE_ENDPOINT`, `STORAGE_PUBLIC_URL`, `STORAGE_REGION`, `STORAGE_BUCKET`, `STORAGE_ACCESS_KEY`, `STORAGE_SECRET_KEY`. `STORAGE_ENDPOINT` is the address internal to the project network; `STORAGE_PUBLIC_URL` is the origin a browser reaches, and it is the one your presigned URLs must carry, or their signature will not verify. The bucket created up front is named after the project's short identifier: a convenient default, not a ceiling. Your application may create others.
  • Postgres extensions: `pgvector`, `pg_trgm`, `pgcrypto`, `uuid-ossp`, `unaccent`, `citext` and `btree_gin` are enabled per project from the Topology tab. Pierrr runs the creation with the privileges required; your database role stays deliberately restricted, so a migration doing `CREATE EXTENSION IF NOT EXISTS` works once the extension is enabled, with no change on your side.

Prisma

Prisma needs a second variable alongside `DATABASE_URL`: `DIRECT_URL`, used only for migrations. It normally exists to bypass a connection pooler (PgBouncer, Supabase…) sitting in front of the database, which does not support some of the DDL commands `prisma migrate` runs. Pierrr's managed Postgres is already a direct connection, with no pooler in front of it, so you can simply point `DIRECT_URL` at the same value as `DATABASE_URL`.

In your `schema.prisma`, declare both variables on the datasource:

prisma
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DIRECT_URL")
}

Then, in your container's entrypoint (the Dockerfile CMD or a startup script in `package.json`), export `DIRECT_URL` from `DATABASE_URL` before running migrations:

bash
export DIRECT_URL="$DATABASE_URL"
npx prisma migrate deploy

No action is needed on Pierrr's side: `DIRECT_URL` is not a managed variable, it lives entirely in your application's code.

Bulk edit (paste a .env)

Instead of adding variables one at a time, bulk edit lets you paste a whole .env block at once. Pierrr diffs your paste against the project's existing variables and applies only the differences.

The Bulk edit button, at the top right of the Secrets tab, next to Add variable.

Step by step:

  1. Open your project, then the Secrets tab. At the top right of the card, click the Bulk edit button (next to Add variable).
  2. In the dialog that opens, paste your .env content into the text area: one variable per line, in KEY=value format. Values may be quoted.
  3. To delete an existing variable, add a line prefixed with an exclamation mark, e.g. !OLD_FLAG.
  4. Read the preview below the text area: it shows how many variables will be added, replaced and deleted, and highlights invalid lines in red (the Apply button stays disabled while any line is invalid).
  5. Click Apply. The variables are saved (new ones are masked by default) and the list updates immediately.
Paste the .env block; the preview live-counts additions, replacements and deletions (and ignored deletions) before you apply.

Example content to paste:

DATABASE_URL=postgres://user:pass@db:5432/app
API_KEY=sk_live_123
# remplace une clé existante, ajoute une nouvelle
NODE_ENV=production
# supprime une variable qui existait
!OLD_FLAG

Merge rules applied to your paste:

  • A key that already exists is replaced with the pasted value.
  • A missing key is added (masked by default).
  • A line prefixed with ! (e.g. !OLD_KEY) deletes the variable if it exists.

The live preview recaps, before you apply, how many additions, replacements and deletions there are, and flags invalid lines to fix.

Backups tab

Open Archive to create or restore a backup. Two types are available:

  • Data backup, a single run for the whole project: every volume of every container, plus a dump of the linked managed database. Give it a name, or one is generated automatically; the history lists each run by that name. Restore overwrites the app's current state; it is destructive and requires confirmation.
  • Pierrr project export, exports the project's structure only (apps, repo bindings, network groups and volume metadata), with no data. Validating an export against another organization checks that it has a matching GitHub App installation for every repository, re-creating the project there is a documented next step, not shipped yet.

A project export can be created, downloaded and validated against another organization. Importing it back is not available yet: the matching button is visible but inactive.

Data backups are not included on the Free plan: the tab offers an upgrade instead. On paid plans, how long archives are kept depends on the plan, and the oldest ones are purged automatically.

  • Replaces the current data, after confirmation
  • On your own server, with Pierrr Agent
A data backup covers the whole project in a single archive: every volume of every container and the database. It runs by hand or on a schedule, and older ones are purged according to your plan's retention. Restoring rewrites the current state.

Scheduling backups

A Schedule accordion sets a recurring backup of the project: daily or weekly, with the day of the week and the run hour in UTC, and an option to get an email after each run. Scheduled backups appear in the same history as the ones you start by hand.

Restoring a backup rewrites the project's volumes and database with the contents of the archive, and restarts the project. The operation destroys the current data, so it asks for a confirmation.

A successful backup can be downloaded, to keep a copy outside Pierrr.

A size history chart tracks your successful backups over time. It shows the latest size, the gap with the previous one and the peak, to spot a growing database. Failed or running backups are not plotted.

The size history of successful backups.

Import my data (SQL dump restore)

At the top of the Backups tab, next to the Backup button, the Import my data button opens a window that restores an existing SQL dump straight into the project's managed database, no SSH or `psql` needed.

  • Format: plain-text `.sql` file only, 15 MB maximum. The binary `pg_dump --format=custom` format is not accepted.
  • Only one import can run at a time per project; retry once the previous one has finished.
  • All or nothing: the dump runs as a single transaction. If anything conflicts with what's already there (a table that already exists, for example), the entire import fails and nothing is applied, so it's safe to retry.
  • A history list shows every import live (filename, size, status: pending, running, succeeded, failed) with the error message visible inline when one fails.
  • The uploaded file's content is encrypted at rest and is never retained once the restore finishes, whether it succeeds or fails.

For a project hosted on your server, the agent checks the file then restores it on the machine, with the project's rights and the same rules. You can also give an https address: your server then downloads the file itself, without it going through Pierrr, and only its host and name stay in the history. It needs agent 0.71.0-beta or later: the button is greyed out with the reason otherwise.

The Import my data window, opened from the Backups tab, with the import history.
For a project on your server, you can also give an address: your server downloads the file itself.
One row per app, each project-wide data backup run.
Data backup covers every volume plus one DB dump, no selection to make. Pierrr project export is structure-only.

Network groups tab

Group the project's containers into isolated networks. Every project starts with a protected internal group that cannot be renamed or removed. Create additional groups with Add, then use Manage members to choose which containers join each group. Available on plans whose network policy is choice; on Free, projects stay locked to the single internal group.

The default internal group plus any custom groups, with their attached container count.

A project's actions menu, the "..." next to Start, Stop and Restart, includes Resync networks: it reattaches every container to its network configuration right now, without waiting for the next deploy. Useful for troubleshooting a container that is not reachable on the network you expect. Available on every plan.

Each network group expands into a traffic panel: incoming and outgoing bandwidth, volume received and sent, the group's CPU and memory, with a bandwidth graph over the last hour. These measurements come from the server hosting the project; until some are available, the panel shows an empty state.

A group's traffic: bandwidth, volume, CPU, memory and last-hour graph.

As with secrets, creating extra groups is reserved for paid plans: on the Free plan the project stays on its single internal group.

Published TCP ports

Some protocols cannot be routed over HTTP. A Published TCP ports card, at the bottom of the Network groups tab, exposes a container port directly on the server's public address. Pick the container, the internal port, and optionally a different public port.

A contiguous range is possible, for a service that needs one, such as the passive mode of a file server.

Two caveats. Every open port is one more exposed surface: publish only what the service genuinely needs. And redeploying a container that publishes a port briefly cuts the service, since a port cannot be held by the old and the new instance at the same time.

How many ports a container may publish depends on the plan, from the Starter plan onwards. The Free plan publishes none.

Database panel

A project's action menu opens, where the instance provides one, an administration panel for the managed database in a new tab: enough to inspect your tables and run a query without installing a client on your machine. You arrive already signed in, and you only see this project's database.

For a project hosted on your server, the agent starts the panel on the machine, next to the database, without opening any port, and the console reaches it through its connection. The database credentials stay on the server; the panel's pages go through Pierrr without being kept. The panel closes after 15 minutes without activity. It needs agent 0.71.0-beta or later: the action is greyed out with the reason otherwise.