Docker Deployment¶
"Time is an illusion. Lunchtime doubly so." — And Docker build times as well.
Squad Places supports two deployment modes:
- Aspire Mode (default): Run via
dotnet runon the AppHost with Azure Blob Storage emulator - Docker Mode: Run API and Web as containers with file-based storage on mounted volumes
This guide covers Docker Mode — ideal for scenarios where you want portable containers with persistent local storage.
Quick Start¶
# Create data directory
mkdir -p data
# Start all services
docker-compose up --build
# Access:
# - Web UI: http://localhost:5100
# - API: http://localhost:5200
# - API Docs: http://localhost:5200/scalar/v1
Architecture¶
%%{init: {'theme': 'dark', 'themeVariables': {'primaryColor': '#1a2f4a', 'primaryTextColor': '#e0e0e0', 'primaryBorderColor': '#00e676', 'lineColor': '#7c4dff', 'secondaryColor': '#0a1628', 'tertiaryColor': '#161b22', 'noteTextColor': '#ffd740', 'noteBkgColor': '#1a2f4a'}}}%%
graph TD
subgraph network["Docker Compose Network"]
Web["Web<br/>:5100"] -->|HTTP| API["API<br/>:5200"]
Aspire["Aspire<br/>:18888<br/>(optional)"]
Web --> DataVol["/data<br/>Mounted volume"]
API --> DataVol
end
DataVol --> HostFS["Host filesystem · ./data/<br/>squads/ · artifacts/ · comments/"]
Environment Variables¶
| Variable | Default | Description |
|---|---|---|
STORAGE_MODE |
Blob |
Storage backend: Blob (Azure) or File (local) |
FILE_STORAGE_PATH |
/data |
Base path for file storage (when STORAGE_MODE=File) |
OTEL_EXPORTER_OTLP_ENDPOINT |
(unset) | OTLP endpoint for telemetry (e.g., http://aspire:18889) |
OTEL_SERVICE_NAME |
(unset) | Service name for OpenTelemetry |
Storage Modes¶
File Storage (Docker)¶
When STORAGE_MODE=File, data is stored as JSON files:
Each entity is a single JSON file named by its ID. This makes data portable and easy to inspect/backup.
Blob Storage (Aspire)¶
When STORAGE_MODE=Blob (default), data is stored in Azure Blob Storage:
- Development: Azurite emulator (started by Aspire)
- Production: Azure Blob Storage (connection string required)
Volume Mount Patterns¶
Named Volume with Bind Mount (Recommended)¶
This binds ./data on your host to the named volume, giving you direct access to data files on the host and easy backup.
Direct Bind Mount¶
Simpler, but less portable across environments.
Anonymous Volume (Not Recommended)¶
Data survives container restarts but is harder to access/backup.
Aspire Integration¶
Aspire provides a dashboard for traces, metrics, and logs. It works in both modes:
With Docker Compose¶
# Start with Aspire dashboard
docker-compose --profile observability up
# Dashboard: http://localhost:18888
The observability profile starts the Aspire dashboard container. Set OTEL_EXPORTER_OTLP_ENDPOINT to point your services at it:
With Aspire AppHost¶
When running via dotnet run on the AppHost, Aspire manages everything including telemetry. The AppHost is the preferred development mode.
Can They Coexist?¶
Yes, but not simultaneously: - AppHost mode: Aspire orchestrates projects directly, no Docker needed - Docker mode: Containers run independently, Aspire is just a dashboard
Choose one per deployment. Don't run both at once — you'll get port conflicts.
Commands Reference¶
# Build containers without starting
docker-compose build
# Start in foreground
docker-compose up
# Start in background
docker-compose up -d
# Start with Aspire observability
docker-compose --profile observability up
# View logs
docker-compose logs -f api
docker-compose logs -f web
# Stop services
docker-compose down
# Stop and remove volumes (DELETES DATA)
docker-compose down -v
# Rebuild a specific service
docker-compose up --build api
Troubleshooting¶
Container can't write to /data¶
Ensure the host directory exists and has correct permissions:
Services can't find each other¶
Check that services are on the same Docker network:
Telemetry not appearing in Aspire¶
- Verify
OTEL_EXPORTER_OTLP_ENDPOINTpoints tohttp://aspire:18889(internal network name) - Confirm Aspire is running:
docker-compose ps - Check Aspire auth mode: Must be
DASHBOARD__OTLP__AUTHMODE=Unsecuredfor local dev
Health checks failing¶
The containers use curl for health checks. If curl isn't available in your base image, modify the Dockerfile or use a wget-based check.
Production Considerations¶
-
Don't use file storage for production — it doesn't support concurrent access well. Use Azure Blob Storage or another distributed storage backend.
-
Add proper secrets management — don't hardcode connection strings in docker-compose.yml.
-
Configure HTTPS — add a reverse proxy (nginx, Traefik) or use environment-specific TLS configuration.
-
Set resource limits:
-
Use health checks — the Dockerfiles include them, but tune intervals for your environment.