# Deploy a project

> Put a static site or an app live at its own HTTPS address, preview the change, read its logs, and take it down.

`sitesolide deploy`, run in a project's folder, puts it live at `https://<slug>.<zone>`. Run it again to ship each change.

```diagram
[Your laptop] sitesolide deploy
-> built code, over SSH or a token
[[Your server]]
  [Unit] generated from the manifest
  [Caddy block] checked with the others
-> restart, then verify over HTTPS
[Live site] https://<slug>.<zone>
```

## Choose a static site or an app

The manifest, `sitesolide.json`, decides which one a project is:

| Kind | Declares | On the server |
|---|---|---|
| Static site | `publicDir`, no `start` | Caddy serves the files, nothing runs |
| App | `start`, or `services` | its own system account, a systemd unit and a Caddy block |

An app can be in any language: anything that listens on its port.

## Write the manifest

`sitesolide detect` prints the manifest it infers from the folder. It recognises a Go module, a FastAPI or Flask app, a JavaScript or TypeScript server, and a generated or static site. Its notes name the secrets the code reads and the network calls it makes.

```bash
sitesolide detect
```

`sitesolide detect --write` writes it as `sitesolide.json`, never over an existing one. Or write it by hand. The smallest static site:

```json
{ "slug": "blog", "publicDir": "public" }
```

The smallest app:

```json
{
  "slug": "api",
  "port": 3030,
  "start": "/usr/local/bin/bun run server.ts",
  "publicDir": "public"
}
```

- `slug` becomes the address: lowercase letters, digits and hyphens.
- `start` is an absolute path: the unit has a minimal `PATH`.
- `port` lies between 3000 and 3099. Left out, `deploy` picks a free one and writes it into `sitesolide.json`: commit it.
- `/` must answer 200, not a redirect, or the deploy fails.

## Preview the deploy

A dry run reads the server and prints the unit and Caddy block an app would get. It changes nothing, does not run the build, and needs the owner's SSH access.

```bash
sitesolide deploy --dry-run
```

## Deploy

The deploy runs the manifest's `build` on your workstation, sends the code, restarts the service, and checks the site over HTTPS:

```bash
sitesolide deploy
```

```text
-> verify
   https://api.example.com/ 200
```

It fails when `/` does not answer, or when a service falls within ten seconds of its restart.

## Check status and logs

`sitesolide status` shows what the server runs, one line per unit, with its memory. `sitesolide logs` shows the project's journal, every service interleaved:

```bash
sitesolide logs --follow
```

## Know what a deploy costs

- **A short 502.** The new code replaces the running version in place: until the new process listens, visitors get a 502.
- **No rollback.** Nothing keeps the previous version. To go back, deploy the previous commit.
- **Caddy comes back.** A block Caddy refuses brings back the previous configuration.
- **Data stays.** A deploy never touches the data folder, but a schema the new version migrated stays migrated.

A static site has no 502: its files are sent in place and nothing restarts.

## Remove a project

> [!WARNING]
> Removal is for good. It deletes the project's units, its served folder with its data, its secret files and its system account. Its backup snapshots are kept.

From the project's folder, `--confirm` takes the slug typed back. `--dry-run` lists every step and removes nothing:

```bash
sitesolide remove --confirm <slug> --dry-run
```

```bash
sitesolide remove --confirm <slug>
```

## Next steps

- [Workers and scheduled jobs](background-work.md): run processes that serve no page.
- [Secrets](secrets.md): give the app its keys without committing them.
- [Custom domains](domains.md): serve the project on its own domain.
- [Manifest reference](manifest.md): every key of `sitesolide.json`.

Full reference: https://github.com/cthiriet/sitesolide/blob/main/docs/manifest.md
