Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 19 additions & 41 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Each directory holds one compose file per source of images:
A third path, [manual setup](#manual-setup), runs the binaries without containers.

!!! info "Both environments can run at once"
They use separate Compose projects (`ignis-http`, `ignis-https`), separate generated container names and separate database volumes, so both can run at once. Each has its own database and so needs its own seeding.
They use separate Compose projects (`ignis-http`, `ignis-https`), separate generated container names and separate database volumes, so both can run at once. Each has its own database and seeds it on its own `up`.

!!! warning "Upgrading from a checkout made before the project rename"
The Compose project names are now `ignis-http` and `ignis-https`, previously `building-simulation` for both, and the fixed container names are gone. An existing stack has to come down first, because the old containers still hold those names. Remove them by name, which reaches nothing but ignis:
Expand All @@ -40,7 +40,7 @@ A third path, [manual setup](#manual-setup), runs the binaries without container

Do not use `docker compose -p building-simulation down`. That targets the project rather than this repository, so on a machine where another service still declares the old project name it removes that service's containers as well, naming neither.

Each environment now has its own database volume, so seed each one once after starting it. The previous shared volume, `building-simulation_ignis-db-data`, is left in place and no longer mounted; remove it with `docker volume rm building-simulation_ignis-db-data` once the new ones are populated.
Each environment now has its own database volume, and each seeds itself on its first `up`. The previous shared volume, `building-simulation_ignis-db-data`, is left in place and no longer mounted; remove it with `docker volume rm building-simulation_ignis-db-data` once the new ones are populated.

If `tentacron-net` already exists from `make tentacron-stack`, `up` fails with a label mismatch, because a network made by `docker network create` carries no Compose labels. Remove it with `docker network rm tentacron-net` while nothing is attached, and the first stack up recreates it correctly.

Expand Down Expand Up @@ -107,7 +107,7 @@ To build from this checkout instead, so local code changes are picked up, drop t

### 2. Verify

`up` seeds an empty database before it starts `ignis`, so there is no separate seed step (see [Seeding the database](#seeding-the-database)). Follow [Verifying](#verifying). The base URL is `http://localhost:8088`.
`up` seeds an empty database before it starts `ignis` (see [Seeding the database](#seeding-the-database)). Follow [Verifying](#verifying). The base URL is `http://localhost:8088`.

---

Expand Down Expand Up @@ -144,7 +144,7 @@ docker compose -f docker-compose.quickstart.yml up -d

This starts `db`, then `ignis` once the database reports healthy, then `proxy` once the app reports healthy. Only the proxy publishes a host port (`HOST_HTTPS_PORT`, default `443`).

Then [Seeding the database](#seeding-the-database) and [Verifying](#verifying).
`up` seeds an empty database before it starts `ignis` (see [Seeding the database](#seeding-the-database)). Then [Verifying](#verifying).

### From source, with a trusted certificate

Expand All @@ -170,36 +170,24 @@ docker compose up -d
!!! warning "CADDY_DATA_DIR"
A wrong value silently produces an untrusted certificate. A missing one fails validation instead, reported by name: `required variable CADDY_DATA_DIR is missing a value`.

Then [Seeding the database](#seeding-the-database) and [Verifying](#verifying).
`up` seeds an empty database before it starts `ignis` (see [Seeding the database](#seeding-the-database)). Then [Verifying](#verifying).

---

## Seeding the database

The TABULA workbook is baked into the `build-db` image, so there is nothing to download. A seed runs in one transaction: if it fails, nothing is committed and any previous tables stay.

`<compose prefix>` below is `docker compose` for `docker-compose.yml`, or `docker compose -f <file>` for the others.

### environment/http

`up` runs `build-db -if-empty` and starts `ignis` only once it exits successfully. An empty database is seeded, a populated one is left unchanged, and a failed seed fails `up` with `service "build-db" didn't complete successfully`.

To rebuild a populated database from the workbook, which drops and recreates all country tables:

```bash
<compose prefix> run --rm build-db -if-empty=false
```
!!! warning "Rebuilding is destructive"
Rebuilding a populated database from the workbook drops and recreates all country tables:

### environment/https

!!! warning "Required before first use, and destructive"
A fresh `db` volume is empty. Seeding drops and recreates all country tables, so it is gated behind the `seed` profile and never runs automatically. Until it has run once, every endpoint that reads the schema will fail.

```bash
<compose prefix> --profile seed run --rm build-db
```
```bash
<compose prefix> run --rm build-db -if-empty=false
```

On a path that pulls images, `--profile seed` is also needed on the `pull`, since profile-gated services are otherwise skipped.
`<compose prefix>` is `docker compose` for `docker-compose.yml`, or `docker compose -f <file>` for the others.

## Verifying

Expand All @@ -222,20 +210,20 @@ Both return `200`. Add `-k` on the https quickstart path, where the certificate
<compose prefix> down -v
```

The `-v` removes the database volume, so the next start seeds again (http) or needs seeding again (https). Omit it to keep the seeded data.
The `-v` removes the database volume, so the next `up` seeds again. Omit it to keep the seeded data.

## Pinning a version

The paths that pull images default to the `latest` published release. To pin a specific one, set `IGNIS_IMAGE_TAG` before starting:

```bash
export IGNIS_IMAGE_TAG=0.2.4-alpha
<compose prefix> --profile seed pull
export IGNIS_IMAGE_TAG=0.7.0
<compose prefix> pull
<compose prefix> up -d
```

!!! info "Tag format"
Published image tags carry no `v` prefix, even though the Git tags do. Release `v0.2.4-alpha` publishes as `0.2.4-alpha`. Export the variable rather than prefixing a single command, so the app and seed images come from the same release.
Published image tags carry no `v` prefix, even though the Git tags do. Release `v0.7.0` publishes as `0.7.0`, the first release whose `build-db` accepts `-if-empty`. Export the variable rather than prefixing a single command, so the app and seed images come from the same release.

---

Expand Down Expand Up @@ -313,7 +301,7 @@ Copy across the whole directory: the compose file, `.env`, the `env/` directory,
| Compose service | Published image | Role |
|---|---|---|
| `ignis` | `ghcr.io/thd-spatial-ai/ignis` | HTTP API server |
| `build-db` | `ghcr.io/thd-spatial-ai/ignis-build-db` | TABULA seeder: on `up` in `environment/http`, `seed` profile only in `environment/https` |
| `build-db` | `ghcr.io/thd-spatial-ai/ignis-build-db` | TABULA seeder, runs on `up` and exits |
| `db` | `postgres:17-alpine` (not built here) | PostgreSQL database |

Service names and image names are independent: the seeder's image keeps the `ignis-` prefix it publishes under. `IGNIS_IMAGE_TAG` pins both `ghcr.io` images to one release. The service is named `ignis` because a service name is registered as a DNS alias on every network it joins, `tentacron-net` included, so it has to be unique across the workspace; `db` and `build-db` never join it and so stay short.
Expand All @@ -340,27 +328,17 @@ Set `IGNIS_IMAGE_TAG` to pin a release. This is strongly advised for any deploym
!!! warning "Set IGNIS_SITE_ADDRESS before deploying"
It defaults to `localhost`. Set it in `env/proxy.env` to the deployment's real domain, or Caddy will neither serve it nor provision a certificate for it. TLS-ALPN-01 (Caddy's default ACME challenge) works entirely over port 443, which is all the compose file publishes, so no port change is needed.

### 4. Pull, start, seed
### 4. Pull and start

For `environment/http`, `up` seeds an empty database itself:
`up` seeds an empty database itself.

```bash
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
```

For `environment/https`:

```bash
docker compose -f docker-compose.prod.yml --profile seed pull
docker compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml --profile seed run --rm build-db
```

!!! warning "Pull first"
With `IGNIS_IMAGE_TAG` unset, a host that already has `latest` cached keeps running the old build after a release, with nothing on that host revealing it. On `environment/https`, the `--profile seed` on the pull is what fetches `build-db`, since profile-gated services are otherwise skipped.

On `environment/https`, seed only on first deployment. Running it against a populated database drops every country table.
With `IGNIS_IMAGE_TAG` unset, a host that already has `latest` cached keeps running the old build after a release, with nothing on that host revealing it.

### 5. Verify

Expand Down
15 changes: 8 additions & 7 deletions environment/https/docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@
# The Caddyfile's site address must match the deployment's real domain, not
# `localhost`, or Caddy will not serve or provision a certificate for it.
#
# Seeding is a one-off admin operation, gated behind the `seed` profile so it
# never runs on `up`. It DROPS and recreates all country tables from the
# TABULA workbook baked into the ignis-build-db image, so it needs no source
# checkout, but run it deliberately and never against a populated production
# database you care about:
# docker compose -f docker-compose.prod.yml --profile seed run --rm build-db
# `up` seeds an empty database from the TABULA workbook baked into the
# ignis-build-db image and leaves a populated one alone. ignis starts only
# after build-db exits successfully, so a failed seed fails `up`. Rebuilding a
# populated database DROPS and recreates all country tables:
# docker compose -f docker-compose.prod.yml run --rm build-db -if-empty=false
#
# No endpoint carries an API key. This is deployed only on a network whose
# access is controlled, and callers are already authenticated by the platform
Expand Down Expand Up @@ -79,6 +78,8 @@ services:
depends_on:
db:
condition: service_healthy
build-db:
condition: service_completed_successfully
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:$$APP_PORT/ignis/health"]
interval: 30s
Expand Down Expand Up @@ -107,7 +108,7 @@ services:

build-db:
image: ghcr.io/thd-spatial-ai/ignis-build-db:${IGNIS_IMAGE_TAG:-latest}
profiles: [seed]
command: ["-if-empty"] # needs an image from v0.7.0 or later
env_file: [env/app.env]
depends_on:
db:
Expand Down
18 changes: 9 additions & 9 deletions environment/https/docker-compose.quickstart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
#
# ---
# Usage: docker compose -f docker-compose.quickstart.yml up -d
# docker compose -f docker-compose.quickstart.yml --profile seed run --rm build-db
# ---
#
# Seed the database once after first start, using the command above.
# build-db is gated behind the `seed` profile so it never runs on `up`:
# it DROPS and recreates all country tables from the TABULA workbook baked
# into its image. The workbook is in the image itself (see
# ignis-db.dockerfile), so seeding needs no source checkout and no manual
# download. Until it has run once, the schema is empty and every endpoint
# that reads it will fail.
# `up` seeds an empty database from the TABULA workbook baked into the
# ignis-build-db image (see ignis-db.dockerfile), so seeding needs no source
# checkout and no manual download. A populated database is left alone, and
# ignis starts only after build-db exits successfully. Rebuilding a populated
# database DROPS and recreates all country tables:
# docker compose -f docker-compose.quickstart.yml run --rm build-db -if-empty=false
#
# Trade-off vs. docker-compose.prod.yml: Caddy's local CA lives in a
# Docker-managed volume (caddy-data) instead of a host bind mount, so it is
Expand Down Expand Up @@ -62,6 +60,8 @@ services:
depends_on:
db:
condition: service_healthy
build-db:
condition: service_completed_successfully
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:$$APP_PORT/ignis/health"]
interval: 30s
Expand All @@ -87,7 +87,7 @@ services:

build-db:
image: ghcr.io/thd-spatial-ai/ignis-build-db:${IGNIS_IMAGE_TAG:-latest}
profiles: [seed]
command: ["-if-empty"] # needs an image from v0.7.0 or later
env_file: [env/app.env]
depends_on:
db:
Expand Down
15 changes: 7 additions & 8 deletions environment/https/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#
# ---
# Usage: docker compose up -d
# docker compose --profile seed run --rm build-db
# ---
#
# Caddy terminates TLS in front of the app and does nothing else: ignis handles
Expand Down Expand Up @@ -66,6 +65,8 @@ services:
depends_on:
db:
condition: service_healthy
build-db:
condition: service_completed_successfully
# No `ports:` mapping here on purpose — the app is not reachable from the
# host directly. Only the reverse proxy is exposed; everyone else must go through it.
# No `command:` override needed — the image's own CMD runs the server
Expand All @@ -77,14 +78,12 @@ services:
retries: 3
start_period: 10s

# One-off DB seed/rebuild job, NOT part of the normal `up` path (see
# `profiles` below). build_db DROPS and recreates all country tables from
# the Excel workbook — invoke deliberately, never automatically on startup:
# docker compose --profile seed run --rm build-db
# No `container_name`: this is only ever started via `run`, which assigns
# its own name, and a fixed one would collide rather than help.
# Seeds an empty database on `up` and exits; a populated one is left alone
# (-if-empty) and ignis waits for it to succeed. Rebuilding a populated
# database DROPS and recreates all country tables:
# docker compose run --rm build-db -if-empty=false
build-db:
profiles: ["seed"]
command: ["-if-empty"]
build:
context: ./../..
dockerfile: environment/ignis-db.dockerfile
Expand Down
4 changes: 2 additions & 2 deletions environment/ignis-db.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

# One-off DB rebuild job, not a long-running service. With no arguments
# build_db DROPS and recreates all country tables from the Excel workbook.
# environment/http runs it on `up` with -if-empty, which leaves a populated
# database alone; the other environments gate it behind the `seed` profile.
# Every compose file runs it on `up` with -if-empty, which leaves a populated
# database alone.
#
# Despite the filename, this builds the `ignis-build-db` seeder image, not
# the database. The database is the `ignis-db` service, plain postgres with
Expand Down
Loading