Deployment Overview
Complete infrastructure and deployment guide for the Ampra platform. Covers local development, Docker containerization, CI/CD pipeline, and production architecture.
Infrastructure Topology
Domain Configuration
| Domain | Service | Port | TLS |
|---|---|---|---|
ampra.solar | React UI (nginx) | 80 | Let's Encrypt via Traefik |
api.ampra.solar | ASP.NET Core API | 80 | Let's Encrypt via Traefik |
docs.ampra.solar | Docusaurus (nginx) | 80 | Let's Encrypt via Traefik |
minio.ampra.solar | MinIO Object Storage | 9000 | Let's Encrypt via Traefik |
emqx.ampra.solar | EMQX Dashboard | 18083 | Let's Encrypt via Traefik |
ampra.solar:1883 | MQTT Broker | 1883 | Raw TCP (direct port expose) |
Container Inventory
| Container | Image | Purpose | Persistent Volume |
|---|---|---|---|
ampra-api | ampra-api:latest | .NET 10 REST API | — |
ampra-ui | ampra-ui:latest | React SPA (nginx) | — |
ampra-docs | ampra-docs:latest | Docusaurus site (nginx) | — |
ampra-ml | ampra-ml:latest | Python ML service | — |
ampra-ingestion | ampra-ingestion:latest | MQTT ingestion worker | — |
ampra-db | postgres:16-alpine | PostgreSQL database | ampra-db-data |
ampra-mongo | mongo:8.0 | MongoDB database | ampra-mongo-data |
ampra-minio | quay.io/minio/minio | S3-compatible storage | ampra-minio-data |
ampra-redis | redis:7-alpine | Cache and job store | ampra-redis-data |
ampra-emqx | emqx/emqx:5.8 | MQTT broker | ampra-emqx-data |
Docker Images
API Image (ampra-api)
Multi-stage build from the solution root:
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# Restores Ampra.Web, Ampra.Application, Ampra.Infrastructure, Ampra.Core
# Publishes release build
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
# Runs as non-root user ($APP_UID)
# Exposes port 80
Build context: Solution root
Dockerfile: src/Ampra.Web/Dockerfile
UI Image (ampra-ui)
# Stage 1: Build
FROM node:20-alpine AS build
# Accepts VITE_API_URL build argument
# Runs npm install + npm build (Vite)
# Stage 2: Serve
FROM nginx:alpine
# Copies dist/ to nginx html directory
# Uses custom nginx.conf for SPA routing
Build argument: VITE_API_URL=https://api.ampra.solar
Build context: src/Ampra.UI/
ML Image (ampra-ml)
FROM python:3.12-slim
# Installs requirements (Flask, XGBoost, scikit-learn, pandas, etc.)
# Runs as non-root 'appuser'
# Serves via Gunicorn (2 workers, 600s timeout)
Build context: src/Ampra.ML/
MQTT Ingestion Image (ampra-ingestion)
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# Restores and publishes Ampra.MqttIngestion + Ampra.Core
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS runtime
# No web server — runs as background worker
Build context: Solution root
Dockerfile: src/Ampra.MQTT/Dockerfile
Docs Image (ampra-docs)
# Stage 1: Build
FROM node:22-alpine AS build
# Runs npm ci + npm run build (Docusaurus)
# Stage 2: Serve
FROM nginx:alpine
# Serves static build with asset caching (1 year for static assets)
Build context: src/Ampra.Docs/
Networking
All containers are connected via the web Docker network. Traefik discovers containers through Docker labels.
Traefik Label Pattern
traefik.enable: true
traefik.http.routers.{name}.rule: "Host(`{domain}`)"
traefik.http.routers.{name}.entrypoints: websecure
traefik.http.routers.{name}.tls.certresolver: myresolver
traefik.http.services.{name}.loadbalancer.server.port: {port}
Inter-Service Communication
| From | To | Protocol | Address |
|---|---|---|---|
| API | PostgreSQL | TCP | ampra-db:5432 |
| API | MongoDB | TCP | ampra-mongo:27017 |
| API | Redis | TCP | ampra-redis:6379 |
| API | MinIO | HTTP | ampra-minio:9000 |
| API | ML Service | HTTP | ampra-ml:5050 |
| EMQX | API | HTTP | ampra-api:80 |
| Ingestion | EMQX | MQTT | ampra-emqx:1883 |
| Ingestion | MongoDB | TCP | ampra-mongo:27017 |
| Ingestion | PostgreSQL | TCP | ampra-db:5432 |
| ML Service | MongoDB | TCP | ampra-mongo:27017 |
| ML Service | Redis | TCP | ampra-redis:6379 |
| ML Service | MinIO | HTTP | ampra-minio:9000 |