---
title: "Provision"
description: "Dokploy projects, S3 buckets, DNS, the Turborepo cache and nightly database backups, from one command."
url: https://plate.findmalek.engineer/docs/provision
markdown: https://plate.findmalek.engineer/docs/provision.md
group: "Infra"
---

# Provision

```bash
findplate provision <dokploy|s3|dns|turbo|backup|all> [--dry-run] [--domain example.com]
```

Everything points at your own infra. Set it once with `findplate config`:

```bash
findplate config set dokployUrl https://dokploy.example.com
findplate config set dnsProvider cloudflare   # or vercel
findplate config set s3Endpoint https://s3.example.com
findplate config set turboCache https://cache.example.com   # optional, Vercel's cache otherwise
findplate config set githubOwner your-name
```

Secrets only come from the environment (or a hidden prompt in a terminal). They are piped to `gh`, never printed and never written to `.findplate/`.

| Target | What it does | Secrets read |
| --- | --- | --- |
| `dokploy` | one Dokploy project per environment (`<name>-production`, `<name>-preview`) | `DOKPLOY_API_KEY` |
| `s3` | a bucket and an access key per environment, saved as GitHub secrets | `GARAGE_ADMIN_TOKEN` with `GARAGE_ADMIN_URL`, or `S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY` for any S3 api |
| `dns` | records for the apex (web), `dashboard`, `api`, `docs` and `*.preview` | `VERCEL_TOKEN` or `CLOUDFLARE_API_TOKEN` |
| `turbo` | `TURBO_TEAM`, `TURBO_API` and `TURBO_TOKEN` in the repo's GitHub settings | `TURBO_TOKEN` |
| `backup` | adds `.github/workflows/backup.yml` and `BACKUP_DATABASE_URL` | `BACKUP_DATABASE_URL` |

`--dry-run` prints the plan and changes nothing. Every step checks what already exists before it creates anything, and finished steps are recorded in `.findplate/provision.json`, so rerunning is safe.

Other flags: `--repo owner/name` (default `githubOwner/<project name>`), `--envs production,preview`, `--dir`.

## DNS

`web` and `dashboard` point at Vercel (`cname.vercel-dns.com`), `api`, `docs` and `*.preview` at the host of your Dokploy url. Set `SERVER_HOST` to use another host or an IP. Existing records with the same name and type are left alone.

## Backups

The `backup` workflow runs nightly at 03:17 UTC: `pg_dump | gzip` into `backups/db-YYYY-MM-DD.sql.gz` in the production bucket. Run it by hand from the Actions tab (`workflow_dispatch`). Expire old dumps with a lifecycle rule on the bucket, the workflow never deletes anything.

### Restore

```bash
aws s3 ls s3://<bucket>/backups/ --endpoint-url $S3_ENDPOINT
aws s3 cp s3://<bucket>/backups/db-2026-01-01.sql.gz . --endpoint-url $S3_ENDPOINT
gunzip -c db-2026-01-01.sql.gz | psql "$DATABASE_URL"
```

Restore into an empty database, or drop the schema first. Try it on a scratch database before you need it.

## Database and S3 browsers

```bash
DOKPLOY_ENVIRONMENT_ID=... BROWSER_PASSWORD=... findplate provision browsers --domain example.com
```

Deploys two extra Dokploy compose apps in the preview environment, at `db.preview.example.com` and `s3.preview.example.com` (set `PREVIEW_DOMAIN` to change the base). The `*.preview` dns record from `provision dns` already covers them.

- `db` is [pgweb](https://github.com/sosedoff/pgweb) started with `--readonly`. Set `BROWSER_DATABASE_URL` to open one database straight away, or leave it empty and paste a preview database url in the connect form.
- `s3` is [s3manager](https://github.com/cloudlena/s3manager) pointed at `s3Endpoint`, using `S3_ACCESS_KEY_ID` and `S3_SECRET_ACCESS_KEY`.

`--readonly` only guards pgweb itself, so give `BROWSER_DATABASE_URL` a read-only postgres role. Both sit behind Dokploy's basic auth (`BROWSER_USER`, default `preview`, and `BROWSER_PASSWORD`), so they are never public. Put the preview server on a tailnet if you want them off the internet entirely. Rerunning updates the compose files and redeploys, the domain and auth are only created once. The images use `latest`, pin a tag in Dokploy once you have one you trust.

The compose, domain and security calls are isolated in `composeApi`. `compose.create`, `compose.update` and `compose.deploy` follow the Dokploy reference, `security.create` and the compose list inside `project.all` are assumed.

## What is verified

The Garage admin paths (`/v2/CreateBucket`, `CreateKey`, `AllowBucketKey`, `ListBuckets`, `ListKeys`) come from its OpenAPI spec, and the S3 calls use AWS signature v4. Dokploy's `x-api-key` header, `project.all` and `project.create` are documented, the response shape of `project.all` is assumed and lives in one function, `dokployApi`.
