Run your own app
Your code as a running service, with a URL, in one call and no image to build.
Recipes: Python app, Node.js app, Bun app, Go app, Ruby app, Rust app, PHP app, Static site
An agent writes a small app (an API, a dashboard, a webhook receiver) and wants it running where someone can open it. A language recipe takes the code as files and runs it: no Dockerfile, no registry, no build to wait for on your side.
Your code is in a repo?
On GitHub, GitLab or another git host: see Run a GitHub repo. The briq downloads the code itself.
Pick your language
| Recipe | Image | Installs at each start | Starts with |
|---|---|---|---|
| python | python:<version>-slim | requirements.txt | python app.py |
| node | node:<version>-slim | package.json | node server.js |
| bun | oven/bun:1-slim | package.json | bun run server.ts |
| go | golang:<version>-alpine | builds the module | the binary |
| rust | rust:1-slim | builds a release | cargo run --release |
| ruby | ruby:<version>-slim | Gemfile | ruby app.rb |
| php | php:<version>-cli | nothing | PHP's built-in server |
| static | nginx | nothing | nginx, serving your files |
Each takes version, files (your code), start, install and build to change the defaults,
env, port (8080) and size. Python, node, bun and ruby also take packages, for dependencies
without a manifest. static takes only files and size, and serves port 80. Without files,
each runs a hello-world app, a quick way to see the shape of it.
One call
briq_run({
recipe: "python",
params: {
files: {
"app.py": "…a Flask app listening on :: and $PORT…",
"requirements.txt": "flask"
}
},
expose: "private"
})
→ bq_9a2… app (python:3.12-slim) running · …internal · url: https://app-m4qa.briq.run/?briq_token=… · cost so far … · sleeps at 18:42 UTC unless used
outputs:
APP_URL=http://…internal:8080
URL=https://app-m4qa.briq.run/?briq_token=…What happens:
- The files are written into the briq under
/appbefore anything starts. requirements.txtis installed, thenpython app.pystarts withPORT=8080.- briq waits until something answers on that port, then the call returns. A first install or
build can outlast the call's two minutes: the briq then shows
creating, andbriq_statustells you when it runs. - The URL was published as the briq was created: nothing restarts to publish it, and with
wait: falseyou get it at once.
With a database
Run the database first, in the same stack, and hand its connection string to the app:
briq_run({ recipe: "postgres", stack: "portfolio" })
→ outputs: DATABASE_URL=postgres://postgres:…@….internal:5432/app
briq_run({
recipe: "python",
stack: "portfolio",
params: {
files: { "app.py": "…reads os.environ['DATABASE_URL']…", "requirements.txt": "flask\npsycopg[binary]" },
env: { DATABASE_URL: { secret: "DATABASE_URL", value: "postgres://postgres:…@….internal:5432/app" } }
},
expose: "private"
})Briqs in one stack reach each other on the private network; only the app gets a URL. The
connection string holds a password, so it goes in as a secret: stored encrypted, shown as
{ secret: "DATABASE_URL" } in status and the dashboard, and kept out of the audit log.
Changing the code
The files you pass are the briq's boot files: written again whenever the process starts over (a
restart, a cold start), so they match what you ran. A resume from a memory snapshot carries on as
it was. To ship a new version, run a new briq with the new files and briq_destroy the old one.
briq_files_put edits a file in place, until the process starts over. Anything else written on
the briq's disk, installed packages included, survives restarts.
When it does not start
If the app exits before it answers (a typo, a missing package, the wrong port), the run fails:
the briq is in error, and its error says how the process exited. briq_logs shows the
traceback. briq_destroy it, fix the files, and run again (the name is free once it is
destroyed).
If it exits later, the briq stops with stoppedReason: "exited" and the code in lastExit.
briq_start runs the same files again; to change them, run a new briq.
Good to know
- Listen on
::, notlocalhost: the private network is IPv6, and::takes IPv4 too. - 256 KB of files in all. A bigger app runs from its repo.
- Data you cannot lose goes in a database briq, or on a
volume. - You already have an image? Run it with
imageinstead of a recipe;files(absolute paths, like/app/server.py),portsandexpose(with a single port) work the same.