# Launching an app

`alf launch` puts the code in a directory on Alf in one go: it works out what the code is, makes the project, adds the databases it uses, writes the two files that describe it, and deploys it.

```bash
cd ~/code/shop
alf launch
```

```
Looking at /Users/ada/code/shop
  A Next.js app. launch writes a Dockerfile for it.
  It uses Postgres (Prisma's schema (postgresql)).
  Before each new version takes over: npx prisma migrate deploy (Prisma's migrations).

The project's name, in Acme? [shop]
This app uses Postgres. Add a Postgres database to the project? (private, backed up every day) [Y/n]
✓ Made the project shop in Acme.
✓ Wrote Dockerfile: how the app is built (an ordinary one; change what you like).
✓ Wrote .dockerignore: what the build leaves out.
✓ Wrote alf.toml: the project as code. Keep it with your code.

Project: shop
  + postgres: new database (postgres:17), private, size xs (256 MB, 0.25 cores), a 1 GB disk, a snapshot every day
  + web: new service, built from Dockerfile, with an address
      release command: npx prisma migrate deploy (before each new version takes over)
      variables: DATABASE_URL (new)
      secrets: AUTH_SECRET (generated)
Sent the code (214 files, 1.2 MB).
From now on, alf deploy deploys it again (commit alf.toml and the Dockerfile with your code).
✓ shop is being deployed:
https://runalf.com/app/projects/prj_8f2c41
```

Once the code is sent, the rest (building it and starting it) happens on Alf: launch says the project is being deployed, ends with its link, and is done. The project's page shows the build and when it's live. `--wait` stays until every service is live, and says what went wrong if one isn't; in CI (where `CI` is set) it waits anyway.

From then on, `alf deploy` deploys it again. Commit `alf.toml` (and the Dockerfile, if launch wrote one) with your code.

You need the [command line](/docs/cli), signed in. Run it without a terminal (an agent, a script) and every question takes its default; `--yes` does the same at a terminal. `--name` says the project's name, `--project` launches into a project you have already instead of making one, `--no-deploy` stops after the files, `--wait` waits for the deploy.

## What it recognises

| It finds | It deploys |
| --- | --- |
| A docker-compose file | Its services, as the file says (see [docker-compose stacks](/docs/compose)). launch writes an `alf.toml` with just the project, for the databases, buckets and settings you add later. |
| A `Dockerfile` | That, as one service. launch still looks at the code for its databases and migrations. |
| **Node.js**: Next.js, Nuxt, Remix and React Router, SvelteKit, Astro, NestJS, Express, Fastify, Hono and other servers with a `start` script; npm, pnpm, Yarn or Bun, in a monorepo too (see below). The Node.js version is what `.nvmrc` or `engines.node` asks for, else the current LTS. | A service, from a Dockerfile it writes. |
| **Static sites**: Vite, Create React App, Vue, Angular, Gatsby, Docusaurus, Eleventy, VitePress, Astro without a server, a Next.js export, Hugo, Jekyll, MkDocs, Blazor WebAssembly, plain HTML | A [static site](/docs/static-sites): built, and its files served from the edge. |
| **Python**: Django, FastAPI, Flask, Streamlit, Gradio, or a `Procfile` | A service (gunicorn, uvicorn, or the framework's own server). |
| **Ruby**: Rails, Sinatra and other Rack apps | A service. |
| **PHP**: Laravel, Symfony, WordPress, plain PHP | A service, served by [FrankenPHP](https://frankenphp.dev). |
| **Elixir**: Phoenix, and other Mix projects | A service, built as a release. |
| **.NET**: ASP.NET Core, and .NET workers | A service. In a solution, its web project. |
| **Java and Kotlin**: Spring Boot, Quarkus, Micronaut, Ktor (Maven or Gradle) | A service. |
| **Go**, **Rust** (a Cargo workspace too), **Deno** | A service. |
| Bots (discord.js, Telegraf, discord.py, aiogram…) and workers with no web server | A service with no address, which always runs (see below). |

The Dockerfile it writes is an ordinary one, commented, with nothing Alf-specific in it: change what you like, and Alf builds what it says. It never writes over a file that's there.

## Monorepos

In a JavaScript monorepo (pnpm, npm, Yarn or Bun workspaces, with or without Turborepo), launch deploys one app of it: the one whose directory you run it in, or, from the workspace's root, the app it finds (asking which when there are several; `--app apps/web` says it). Libraries, VS Code and browser extensions, command-line tools and shared settings are left out, and it says so.

The app is built from the workspace's root, since it uses the workspace's other packages and the lockfile is there: the Dockerfile goes in the app's directory (`apps/web/Dockerfile`), the `.dockerignore` at the root, and alf.toml says `context` is the root. The whole workspace is installed (with pnpm, its packages are fetched from the lockfile first, so that step is redone only when the lockfile changes), then the app and the packages it uses are built: through Turborepo when the workspace has it (`turbo run build --filter=web...`), otherwise in order. The app runs from its own directory, or, for a static export, its files are served.

An app with a Dockerfile of its own is built with that one: from the workspace's root when it copies the workspace in (it names the lockfile, `apps/` or `packages/`, or runs `turbo prune`), otherwise in its directory.

To launch another app of the same monorepo as its own project, run launch in its directory.

## Apps side by side

A repository that keeps its apps in directories of their own, with nothing to run at its root (`frontend/` and `backend/`, or `apps/web` and `services/api`), is launched as one project with a service for each, called after its directory and built from it. Directories like `docs/`, `scripts/` and `tests/` are left out.

Where one app's `.env.example` points at another on a laptop (`VITE_API_URL=http://localhost:8000/api` in the frontend, with the backend answering on 8000), it gets the other's address instead: `VITE_API_URL = "${{backend.URL}}/api"`.

## Workers, schedulers and bots

An app's background jobs run beside it, as services of their own: the same code and variables (its secrets by reference, so there's one of each), each with its own command, private and always running. launch finds them in:

- a `Procfile`: each line besides `web` is a process of its own (`worker: …`, `clock: …`), and `release` is the release command;
- Sidekiq, GoodJob, Delayed Job or Resque in a Ruby app (with Solid Queue, Rails 8's own way: in Puma, with `SOLID_QUEUE_IN_PUMA`);
- Celery (its worker, and `beat` where it has a schedule) or RQ in a Python app;
- Laravel's queue (`app/Jobs`, or Horizon) and its scheduler;
- a `worker` script in `package.json`. An app that uses BullMQ or the like without one is told to add it.

A program that serves nothing over HTTP (a Discord or Telegram bot, a queue's consumer, a `Procfile` with no `web` line) has no address and no port, and always runs: it's called `bot` or `worker` instead of `web`.

## Launching into a project you have

`--project` launches into a project you have already. Its services keep their names: if it has a `web` already, the app is called after its directory instead (launch asks, at a terminal), so it's a service of its own beside the other one, not a new version of it.

## Databases

launch looks at what the code depends on (`pg`, Prisma's schema, `psycopg`, the `pg` gem, `DB_CONNECTION` in Laravel's `.env.example`…) and asks once about each database it finds: Postgres, MySQL, Redis or MongoDB. A yes adds it to the project ([private, with a disk and daily snapshots](/docs/databases)) and gives the app its address as a variable that's kept up to date: `DATABASE_URL = "${{postgres.DATABASE_URL}}"` (Laravel, WordPress, .NET and the Java frameworks get the variables they read instead, like `ConnectionStrings__DefaultConnection`).

An app on **SQLite** (Django's default, `better-sqlite3`, Prisma's `sqlite`, a Go or Rust driver) keeps its database file on a [volume](/docs/volumes) at `/data`, so a deploy doesn't start it afresh: launch links the file the code names (`db.sqlite3`, `./data/app.db`) there, and migrates it as the app starts, since a release command can't see the volume. A volume ties the app to one server; a Postgres database doesn't.

## Migrations

When the code has migrations launch knows how to run, it sets them as the service's [release command](/docs/deployments#release-commands): run once per deploy, before the new version takes over, so the new code never meets an old database. It recognises a `db:migrate` or `migrate` script, Prisma, Drizzle, Knex, Sequelize, Django, Alembic, Flask-Migrate, Rails, Laravel and Doctrine.

## Secrets and variables

Secrets the framework needs are generated (`AUTH_SECRET`, `SECRET_KEY_BASE`, Django's `SECRET_KEY`, Laravel's `APP_KEY`, WordPress's keys and salts…). If the code has a `.env.example`, launch reads it: its own secrets are generated, other people's (API keys, passwords) are asked for at the first deploy, and settings keep their example values unless those only work on your machine. Those, and settings the example leaves empty, are asked for at the terminal (Enter skips one); with nobody there, launch names them so you can `alf env set` them. Variables Alf sets itself, like `PORT`, and switches meant for a laptop, like `APP_DEBUG`, are never taken from it. A Rails app's `config/master.key` can go in as the secret `RAILS_MASTER_KEY`. None of the values is ever written to a file.

Variables a frontend's build bakes into the browser's code (`VITE_…`, `NEXT_PUBLIC_…`, `NUXT_PUBLIC_…`, `PUBLIC_…`, `REACT_APP_…`) are given to the build, for a static site and an app that runs alike: the Dockerfile launch writes takes each one the `.env.example` names with an `ARG` line. Changing one builds the app again; a variable you add later needs an `ARG` line of its own.

## A directory with an alf.toml already

Run in a directory whose `alf.toml` names a project you don't have (someone else's code, another organization), launch makes that project and deploys it, databases and all. If the project is there, it says so and leaves it to `alf deploy`.
