Skip to main content

Services

This page is the configuration reference for every container in the CDT stack. Each service has its own section with prerequisites, installation notes, configuration, and integration details. For an overview of how the services fit together and how to start the stack, see Self-Hosting Overview.

All services start together via the project's docker-compose.yml:

docker compose up -d --build

On first run, apply database migrations once PostgreSQL is healthy:

docker compose run --rm migrate

PostgreSQL

Image: postgres:15 Purpose: Relational database storing application data — building records, object metadata, users, and other structured entities.

Installation

The image is pulled automatically when the Compose stack starts. On a fresh database, run Prisma migrations once after PostgreSQL is healthy:

docker compose run --rm migrate

This creates all required application tables. If the postgres_data volume already exists from a previous deployment, skip this step.

After migrations, the database contains schema only. Seed initial data as needed by restoring a database dump, executing SQL INSERT statements from CSV/JSON datasets, or running dedicated seeding scripts.

Configuration

All credentials are supplied via environment variables (.env file in the project root). They are only used during initial database creation when the data volume is empty — changing them after initialization does not modify an existing database.

VariablePurpose
POSTGRES_USERDatabase user created on init
POSTGRES_PASSWORDPassword for the database user
POSTGRES_DBName of the application database

Persistent storage: Volume postgres_data mounted at /var/lib/postgresql/data, managed by Docker. Data persists across container restarts, updates, and redeployments.

Network and port: PostgreSQL listens on port 5432, exposed to the host via Docker port mapping. Within the Compose stack, other services reach it at hostname postgres on port 5432.

Production note: If PostgreSQL is only accessed by services inside the same Compose stack, remove the host port mapping for improved security. If external access is required, restrict it to trusted internal IPs:

services:
postgres:
ports:
- "192.XXX.X.X:5432:5432"

Health check: The Compose file defines a health check (pg_isready -U <POSTGRES_USER> -d <POSTGRES_DB>, 10s interval, 5s timeout, 5 retries) so dependent services wait until PostgreSQL is ready.

Integration

DATABASE_URL is used by the CDT backend to connect:

postgresql://<POSTGRES_USER>:<POSTGRES_PASSWORD>@postgres:5432/<POSTGRES_DB>

The hostname postgres resolves through Docker Compose internal networking. Port 5432 must not be publicly accessible in production — restrict inbound access to trusted internal networks only.

MinIO

Image: minio/minio:latest Purpose: S3-compatible object storage for unstructured digital twin assets — BIM models, point clouds, GIS data, images, and videos.

Installation

MinIO starts as part of the CDT Compose stack. A one-time minio-init service runs on first startup to:

  1. Wait for MinIO to become available
  2. Create the platform's storage buckets (for example, users and building-footprints) if they do not exist
  3. Exit automatically after initialization

No manual bucket creation is needed.

Configuration

VariablePurpose
MINIO_ROOT_USERAdministrator username
MINIO_ROOT_PASSWORDAdministrator password

Ports:

PortPurpose
9000S3-compatible API endpoint
9001Web-based management console

Persistent storage: Volume minio_data mounted at /data. Data persists across restarts and redeployments.

Health check: MinIO's built-in health endpoint is used to verify service readiness before dependent services start.

Integration

  • Port 9000 (API): Used by the CDT backend and frontend viewers to upload, retrieve, and stream digital twin assets. Must be protected by TLS (HTTPS) in production.
  • Port 9001 (Console): Browser-based management UI. Restrict access to trusted administrator networks or IP addresses only.

Production deployment: Place MinIO behind a reverse proxy (e.g. Nginx or Traefik) with valid TLS certificates. Without TLS, mixed-content warnings may occur when MinIO is accessed from secure frontend applications.

The Node application connects to MinIO using these environment variables:

VariablePurpose
MINIO_ENDPOINTMinIO hostname or IP
MINIO_USE_SSLtrue in production
S3_ACCESS_KEYAccess key
S3_ACCESS_SECRETSecret key
NEXT_PUBLIC_MINIO_BUCKET_URLPublic bucket base URL

Martin / PostGIS

Images: postgis/postgis:15-3.4 · maplibre/martin:main Purpose: PostGIS extends PostgreSQL with geospatial capabilities. Martin reads PostGIS spatial tables and exposes them as Mapbox Vector Tiles (MVT) consumed by the CDT map frontend.

Installation

Both images are pulled automatically when the Compose stack starts. On first startup:

  • PostgreSQL initializes the database using POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB
  • The postgis/postgis image enables PostGIS extensions automatically
  • Any SQL scripts placed in /docker-entrypoint-initdb.d are executed during initialization
  • Martin waits until PostgreSQL reports healthy, then connects and auto-discovers all spatial tables and views containing geometry columns
  • Each discovered dataset is immediately exposed as a vector tile endpoint — no manual registration needed

Configuration

PostGIS shares the PostgreSQL configuration described in the PostgreSQL section — credentials and storage are the same.

Martin is configured via a PostgreSQL connection string passed as a command argument:

postgresql://<POSTGRES_USER>:<POSTGRES_PASSWORD>@postgres:5432/<POSTGRES_DB>
SettingValue
Internal port3000
Host port6080
Log level env varRUST_LOG=info
Startup dependencyWaits for PostgreSQL health check

Martin endpoint once running:

http://localhost:6080

Integration

  • Port 5432 (PostgreSQL/PostGIS): Stores spatial and non-spatial application data. Accessed by both the Node backend and Martin.
  • Port 6080 (Martin): Serves Mapbox Vector Tiles consumed by MapLibre in the CDT frontend. Used for spatial visualization of organizational datasets.

Security:

  • PostgreSQL must not be publicly accessible in production
  • Martin should be deployed behind a reverse proxy with TLS enabled
  • Database credentials are supplied via environment variables only
  • Martin operates with read-only access to spatial datasets

Node / Next.js Application

Image: Built from the project Dockerfile Purpose: Core backend and web application. Provides business logic, CRUD operations for all entities, authentication via NextAuth.js, and serves the Next.js frontend.

Installation

Build and start the application container:

docker compose up -d --build

Verify the application is running:

http://localhost:6012

A container health check validates that the application responds on its internal HTTP endpoint.

The Node service depends on PostgreSQL being healthy. On first deployment, run migrations before the application starts serving traffic:

docker compose run --rm migrate

Configuration

The application is configured entirely through environment variables in the .env file at the project root. The table below mirrors the keys present in the current .env template — values marked sensitive must be set to secure values for your environment. Do not commit real secrets to source control.

KeyPurpose
DATABASE_URLPostgreSQL connection string
MINIO_ENDPOINTMinIO hostname
MINIO_USE_SSLtrue in production
MINIO_REGIONS3 region (typically us-east-1)
MINIO_URLFull MinIO base URL
S3_ACCESS_KEYMinIO access key
S3_ACCESS_SECRETMinIO secret key
AUTH_SECRETNextAuth.js signing secret
AUTH_TRUST_HOSTtrue when behind a reverse proxy
AUTH_URLPublic domain name of the application
AUTH_GOOGLE_IDGoogle OAuth client ID
AUTH_GOOGLE_SECRETGoogle OAuth client secret
NEXT_PUBLIC_RECAPTCHA_SITE_KEYGoogle reCAPTCHA site key (public)
NEXT_PUBLIC_RECAPTCHA_SECRET_KEYGoogle reCAPTCHA secret key
EMAIL_HOSTSMTP hostname (e.g. smtp.resend.com)
EMAIL_PORTSMTP port (e.g. 465)
EMAIL_FROMSender address
EMAIL_USERSMTP username
EMAIL_PASSSMTP password
MEMCACHE_SERVERMemcache server address
MEMCACHE_USERNAMEMemcache username
MEMCACHE_PASSWORDMemcache password
NEXT_PUBLIC_GEOCODE_EARTH_API_KEYGeocode Earth API key for address search
NEXT_PUBLIC_MARTIN_SERVER_URLMartin tile server base URL
NEXT_PUBLIC_MINIO_BUCKET_URLMinIO bucket public base URL
NEXT_PUBLIC_ORGANIZATIONAL_DATASETS_URLOpen datasets base URL
NEXT_PUBLIC_POINTCLOUD_API_URLPoint cloud API base URL

Integration

Port 6012 → 3000: Primary HTTP endpoint. Host port 6012 maps to container port 3000.

In production, place the application behind a reverse proxy with TLS and restrict direct access to port 6012 according to your organization's security policies.

External service dependencies:

ServiceConnectionUsed for
PostgreSQLDATABASE_URLAll application data
MinIOS3_ACCESS_KEY / S3_ACCESS_SECRETAsset storage
MartinNEXT_PUBLIC_MARTIN_SERVER_URLMap tile consumption

Notable libraries:

  • NextAuth.js — authentication and session management
  • MapLibre — interactive vector map rendering
  • Prisma — database schema management and query layer

For a full dependency list see package.json in the application source.

Open Data Service

Purpose: Registry of open data portals — municipal, provincial, organizational, and federal. Enables dataset discovery and integration via geographic and categorical filtering.

Installation

No separate installation required. The service is provisioned via Prisma migrations and exposed through the existing Node API.

# 1. Start PostgreSQL
docker compose up -d postgres

# 2. Run migrations (creates the OpenDataPortal table)
docker compose run --rm migrate

# 3. Start the CDT application
docker compose up -d cdt

Configuration

The OpenDataPortal table is created by Prisma migrations. Its schema:

ColumnTypePurpose
idInteger (auto-increment)Unique identifier
nameStringPortal name
groupEnumMUNICIPALITY · PROVINCAL · FEDERAL · ORGANIZATIONAL
provinceEnumTwo-letter province/territory code (e.g. ON, QC)
municipalityStringMunicipality name (optional)
portalUrlString (URL)Portal web link (optional)
apiUrlString (URL)API endpoint (optional)
dataManagementSystemEnumCKAN · SOCRATA · OPENGOV · CUSTOM · OTHER

To inspect or seed portal records using Prisma Studio:

npx prisma studio

Integration

Port 6012 (Node Application): REST API endpoint for portal metadata.

OperationAccess
GET (retrieve portals by province, municipality, group, or ID)Public
POST / PATCH / DELETEAuthenticated users only