Docker Deployment
Craft runs as a set of Docker containers orchestrated by Docker Compose. There are two profiles — dev for local development with hot reload, and prod for production with built images and no volume mounts.
Dev vs. prod profiles
Section titled “Dev vs. prod profiles”The CRAFT_MODE variable in your .env does not control which containers run — you choose the profile explicitly with make dev or make prod (or docker compose --profile dev/prod).
api-dev— FastAPI with source mounted frompackages/api/src, auto-reload on saveweb-dev— Astro dev server with HMR, source mounted frompackages/web/srcdocs-dev— Starlight dev server with HMR
Volume mounts mean you edit files on the host and see changes immediately.
api-prod— Built Python image, no volume mounts, no reloadweb-prod— Static Astro build served by the production serverdocs-prod— Static Starlight build
Containers run from built images only. No host filesystem access.
Both profiles share the same infrastructure containers (no profile required):
- db — PostgreSQL 17 (TimescaleDB HA image) with pgvector and pgai extensions
- pgai-install — One-shot container that installs the pgai extension, then exits
- vectorizer-worker — Continuously generates embeddings for new or updated catalog objects
Fresh deployment
Section titled “Fresh deployment”-
Clone the repository
Terminal window git clone git@git.supported.systems:warehack.ing/astrolock.git ~/astrolockcd ~/astrolock -
Create your
.envfrom the templateTerminal window cp .env.example .envEdit
.envand set at minimum:Terminal window CRAFT_MODE=prodCRAFT_DOMAIN=space.your-domain.comPOSTGRES_PASSWORD=a-strong-passwordGPU_API_KEY=your-embedding-api-keyIf you have Space-Track credentials for supplementary TLE data:
Terminal window SPACETRACK_USER=your-usernameSPACETRACK_PASS=your-password -
Build and start the containers
Terminal window make prodOr equivalently:
Terminal window docker compose --profile prod up --build -d -
Run database migrations
Terminal window docker compose exec api-prod alembic upgrade head -
Seed the catalog (TLEs, celestial objects, radio frequencies)
Terminal window docker compose exec api-prod python -m astrolock_api.seed -
Backfill search text for embeddings
Terminal window docker compose exec api-prod python -m astrolock_api.backfill_search_text -
Trigger initial launch data fetch
Terminal window docker compose exec api-prod python -c "import urllib.request, jsonreq = urllib.request.Request('http://127.0.0.1:8000/api/launches/refresh', method='POST')resp = urllib.request.urlopen(req)print(json.loads(resp.read()))" -
Verify the deployment
Terminal window curl https://space.your-domain.com/api/health# {"status": "ok", "service": "craft-api"}curl https://space.your-domain.com/api/satellites/25544
Updating an existing deployment
Section titled “Updating an existing deployment”cd ~/astrolockgit pulldocker compose --profile prod up --build -d api-prod web-prod docs-prodIf there are new database migrations:
docker compose exec api-prod alembic upgrade headCaddy reverse proxy
Section titled “Caddy reverse proxy”Craft uses caddy-docker-proxy for TLS termination and routing. The Docker Compose labels on each service container tell Caddy how to route traffic:
/api/*and/ws/*route to the API container on port 8000/docs/*routes to the docs container on port 3000- Everything else routes to the web container on port 4321
WebSocket paths (/ws/*) have special proxy settings to prevent Caddy from closing idle connections:
caddy.handle_1.0_reverse_proxy.flush_interval: "-1"caddy.handle_1.0_reverse_proxy.transport.read_timeout: "0"caddy.handle_1.0_reverse_proxy.transport.write_timeout: "0"TLS certificates
Section titled “TLS certificates”TLS is automatic via DNS challenge (ACME + your DNS provider’s API). When you deploy with a new subdomain, expect up to about 2 minutes for Let’s Encrypt to issue the certificate. During that window, HTTPS requests will fail with TLS errors — no manual intervention is needed, just wait.
Monitoring
Section titled “Monitoring”Vectorizer progress
Section titled “Vectorizer progress”After seeding, the vectorizer worker processes all catalog objects to generate embeddings. For 22,000+ objects, this takes roughly 10 minutes. Check progress:
docker compose exec db psql -U astrolock -d astrolock \ -c "SELECT * FROM ai.vectorizer_status;"Embedding counts
Section titled “Embedding counts”Verify that all embedding stores are populated:
docker compose exec db psql -U astrolock -d astrolock -c "SELECT 'satellite' as type, COUNT(*) FROM satellite_embedding_storeUNION ALL SELECT 'celestial', COUNT(*) FROM celestial_object_embedding_storeUNION ALL SELECT 'frequency', COUNT(*) FROM target_frequency_embedding_storeUNION ALL SELECT 'launch', COUNT(*) FROM rocket_launch_embedding_store;"Container logs
Section titled “Container logs”# All containersdocker compose logs -f
# API onlydocker compose logs api-prod --tail 50 -f
# Vectorizer workerdocker compose logs vectorizer-worker --tail 50 -fHealth check
Section titled “Health check”The API exposes health endpoints at both /health (internal, for Docker healthcheck) and /api/health (external, through Caddy):
# Internal (from within Docker network)curl http://api-prod:8000/health
# External (through Caddy)curl https://space.your-domain.com/api/healthDatabase management
Section titled “Database management”Shell access
Section titled “Shell access”docker compose exec db psql -U astrolock -d astrolockFull reset
Section titled “Full reset”docker compose --profile prod down -vdocker compose --profile prod up --build -ddocker compose exec api-prod alembic upgrade headdocker compose exec api-prod python -m astrolock_api.seeddocker compose exec api-prod python -m astrolock_api.backfill_search_textEnvironment variables reference
Section titled “Environment variables reference”The key variables in .env:
| Variable | Default | Purpose |
|---|---|---|
CRAFT_MODE | dev | Not used by Compose directly; documentation convention |
CRAFT_DOMAIN | — | Domain for Caddy TLS and routing |
POSTGRES_PASSWORD | — | PostgreSQL password (required) |
GPU_API_KEY | — | API key for the embedding endpoint |
GPU_BASE_URL | https://astrolock.gpu.supported.systems/v1 | OpenAI-compatible embedding endpoint |
GPU_EMBED_MODEL | mxbai-embed-large | Embedding model name |
OBSERVER_LAT / OBSERVER_LON / OBSERVER_ALT | 36.0 / -86.0 / 200 | Default observer location |
ROTOR_DEFAULT_HOST / ROTOR_DEFAULT_PORT | 127.0.0.1 / 4533 | Default rotctld connection |
ROTOR_MIN_ELEVATION | 5.0 | Minimum elevation for rotor commands (degrees) |
SPACETRACK_USER / SPACETRACK_PASS | — | Optional Space-Track credentials |
VITE_CESIUM_TOKEN | — | CesiumJS ion access token for the 3D globe |
VITE_API_URL / VITE_WS_URL | — | API and WebSocket URLs for the frontend |