# Run your own app

URL: https://briq.cloud/docs/guides/run-your-own-app

> Your code as a running service, with a URL, in one call and no image to build.

Recipes:

- [Python app](https://briq.cloud/cookbook/python.md): Your Python code running as a service, from files or a GitHub repo: it installs requirements.txt and starts it. No image to build.
- [Node.js app](https://briq.cloud/cookbook/node.md): Your Node.js code running as a service, from files or a GitHub repo: it installs package.json and starts it. No image to build.
- [Bun app](https://briq.cloud/cookbook/bun.md): Your TypeScript or JavaScript running on Bun, from files or a GitHub repo: it installs package.json and starts it. No build step.
- [Go app](https://briq.cloud/cookbook/go.md): Your Go code running as a service, from files or a GitHub repo: it builds the module and starts the binary. No image to build.
- [Ruby app](https://briq.cloud/cookbook/ruby.md): Your Ruby code running as a service, from files or a GitHub repo: it bundles the Gemfile and starts it. No image to build.
- [Rust app](https://briq.cloud/cookbook/rust.md): Your Rust crate running as a service, from files or a GitHub repo: it builds a release and runs it. No image to build.
- [PHP app](https://briq.cloud/cookbook/php.md): Your PHP code served by PHP's built-in server, from files or a GitHub repo. No image to build.
- [Static site](https://briq.cloud/cookbook/static.md): HTML, CSS, JavaScript and images served by nginx, from files or a folder of a GitHub repo. No build, no image.

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.

<Callout title="Your code is in a repo?">
  On GitHub, GitLab or another git host: see [Run a GitHub repo](https://briq.cloud/docs/guides/run-a-github-repo). The
  briq downloads the code itself.
</Callout>

## Pick your language

| Recipe                     | Image                     | Installs at each start | Starts with               |
| -------------------------- | ------------------------- | ---------------------- | ------------------------- |
| [python](https://briq.cloud/cookbook/python) | `python:<version>-slim`   | `requirements.txt`     | `python app.py`           |
| [node](https://briq.cloud/cookbook/node)     | `node:<version>-slim`     | `package.json`         | `node server.js`          |
| [bun](https://briq.cloud/cookbook/bun)       | `oven/bun:1-slim`         | `package.json`         | `bun run server.ts`       |
| [go](https://briq.cloud/cookbook/go)         | `golang:<version>-alpine` | builds the module      | the binary                |
| [rust](https://briq.cloud/cookbook/rust)     | `rust:1-slim`             | builds a release       | `cargo run --release`     |
| [ruby](https://briq.cloud/cookbook/ruby)     | `ruby:<version>-slim`     | `Gemfile`              | `ruby app.rb`             |
| [php](https://briq.cloud/cookbook/php)       | `php:<version>-cli`       | nothing                | PHP's built-in server     |
| [static](https://briq.cloud/cookbook/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

```text
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:

1. The files are written into the briq under `/app` **before** anything starts.
2. `requirements.txt` is installed, then `python app.py` starts with `PORT=8080`.
3. 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`, and `briq_status`
   tells you when it runs.
4. The URL was published as the briq was created: nothing restarts to publish it, and with
   `wait: false` you get it at once.

## With a database

Run the database first, in the same stack, and hand its connection string to the app:

```text
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 `::`**, not `localhost`: 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 `image` instead of a recipe; `files` (absolute
  paths, like `/app/server.py`), `ports` and `expose` (with a single port) work the same.
