Build
Apps
On this page
What an app is
An app is a program with a server: source code plus a Procfile, a small file that says how to start each part of the program. Valet builds the code, runs it, and serves it at its own address. Use an app when an artifact isn't enough: the tool has to save data, keep a secret, or run code somewhere other than the browser.
A Procfile has one line per process type:
web: node server.js
worker: node jobs.jsThe web process gets the app's traffic. It must listen on the port
named in the PORT env var. Other types, such as worker, run in the
background.
Valet builds Node.js, Python and Go apps. It detects the language from
files in your code, such as package.json, requirements.txt or
go.mod. Ruby, PHP, Java and .NET aren't supported.
Apps follow the Twelve-Factor App principles for software that runs as a service:
- Dependencies are declared in the code, in files such as
package.json, and the build installs them. - Settings and secrets come from env vars, not from files in the code.
- A database or an LLM is a resource the app reaches through env vars.
- Each build becomes a numbered release.
- Processes keep no data on local disk, start and stop at any time, and scale by running more of a type.
- The app writes its logs to standard output and standard error, and Valet collects them.
Publish an app
Ask your agent:
Publish this as a Valet app called shop.Your agent publishes the source:
publish_app
{
"name": "shop",
"title": "Shop",
"description": "Takes orders for team merchandise.",
"files": [
{ "path": "Procfile", "content": "web: gunicorn app:app" },
{ "path": "requirements.txt", "content": "flask==3.1.0\ngunicorn==23.0.0\n" },
{ "path": "app.py", "content": "from flask import Flask\n\napp = Flask(__name__)\n…" }
]
}publish_app carries text files only, and needs a Procfile at the
root. With the CLI, valet apps create makes the app and links the
folder, and valet deploy uploads every file in the folder except
.git and .valet. Either way, Valet then builds the app and starts
it.
The app serves at https://<name>.<org>.valet.run, such as
https://shop.acme.valet.run. Its address shows an error page until
the first build is running.
Builds
A build fetches your code, detects the language, installs its dependencies, runs its build step and packages the result. A build can't read the app's env vars, so a build step can't use a secret.
publish_app returns before the build finishes, with the build's ID.
Your agent follows it with get_build, which returns the build's
state, the end of its log and the release it made. valet deploy
prints the build log as it runs, then each process as it starts, then
the address.
When a build fails, ask your agent:
The shop build failed. Read the build log and fix the cause.A build stops after 15 minutes, and the built app can be at most 512 MiB. Both are defaults.
Releases and rolling back
Each successful build becomes a release: v1, v2 and so on. Changing
an env var or attaching a resource also makes a new release.
list_releases
{ "name": "shop" }There is no rollback command. To roll back, publish an earlier release's code again; it becomes a new, higher release. Ask your agent:
Roll shop back to v4.It finds the release with list_releases, reads its files with
get_source, then publishes them.
Processes and scaling
The formation is how many processes of each type run. The first
deploy starts one web process and none of the others, so scale a
worker up yourself:
scale_service
{ "name": "shop", "process_type": "worker", "count": 1 }
get_service
{ "name": "shop" }Each type runs 0 to 10 processes.
You can't choose a process size. By default each process has 2 shared CPUs and 512 MB of memory.
Idling and waking
A web process that gets no requests stops, and the next request
starts it again. That request waits while the app starts, for up to
75 seconds. The app must start listening on its port within 60
seconds.
By default a web process stops about 35 minutes after its last
request. Other process types never idle; they run until you scale them
to 0.
A process loses its local files when it stops or restarts. Keep data in a database.
Who can open it
Apps have the same three access modes as artifacts: private (organization members, after they sign in to Valet), password and public. New apps are private.
set_service_access
{ "name": "shop", "kind": "app", "mode": "password", "password": "<password>" }set_service_access takes the password as an argument, so it appears
in the conversation. To keep it out, set it in the dashboard. You can't
share an app with one person by email.
Env vars and secrets
An env var is a named setting the app reads when it starts, such as an API key. Ask your agent:
Add my Stripe key to shop as STRIPE_KEY.set_env_vars
{ "name": "shop", "vars": { "STRIPE_KEY": "sk_live_..." } }Values are secret by default: Valet stores them encrypted and never
shows them back. "secret": false, or --plain in the CLI, stores a
value you can read back, such as a region. An app reads both kinds as
ordinary env vars. Setting one restarts a running app.
A value set over MCP passes through the conversation. To keep a secret out of it, enter the value in the dashboard instead.
Add a database
An app can attach a resource: a database Valet runs for it. Ask your agent:
Add a Postgres database to shop.attach_resource
{ "name": "shop", "resource": "shop-db", "provider": "valet-postgresql", "plan": "dev" }Creating the database takes about a minute. Over MCP, the first call starts it, and your agent repeats the call until the database is attached. Valet then restarts the app with these env vars:
DATABASE_URL
- Holds
- The connection address, with its TLS settings
DATABASE_POOLED_URL
- Holds
- An address for many short connections
DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD
- Holds
- The same details, one per variable
Connect with DATABASE_URL. Destroying the app keeps the database;
destroying the database deletes its data.
See Resources for how administrators manage them.
Logs and restarts
get_logs
{ "name": "shop", "kind": "app" }
restart_service
{ "name": "shop" }get_logs returns recent log lines, and valet logs -f keeps printing
new ones as they arrive. Valet keeps logs for 72 hours.
A process that crashes restarts on its own, up to four times. After
that Valet stops it, and valet ps shows the reason.
Rename or remove
rename_service
{ "name": "shop", "new_name": "store" }
destroy_service
{ "name": "store" }Renaming moves the app to the new address; the old one stops working. Destroying takes the app down at once, with no confirmation, and keeps its resources.
What apps can't do
- Hold connectors. Use an artifact or an agent to reach a connector's tools.
- Know who is visiting. Valet checks access, but it doesn't tell the app who the visitor is.
- Keep files on disk. A process loses its files when it stops.
- Take longer than 60 seconds to answer. A response that hasn't started by then gets an error page.
- Have channels. Only agents have channels.
- Run one-off processes. Valet runs only the Procfile's process
types, and has no release step. Run a task such as a database
migration from one of them, for example when
webstarts.