⚠️ Porta is in beta — APIs and features may change before v1.0
Skip to content

Infrastructure

Last Updated: 2026-04-25

Overview

Porta's infrastructure is designed around Docker containers with PostgreSQL and Redis as backing services. The build pipeline produces a minimal Alpine-based container image (~200MB) suitable for production deployment.

Docker Architecture

Development Stack

The development Docker Compose (docker/docker-compose.yml) provides three services:

ServiceImagePortPurpose
PostgreSQLpostgres:16-alpine5432Primary data store
Redisredis:7-alpine6379Cache, sessions, rate limiting
MailHogmailhog/mailhog1025 (SMTP) / 8025 (UI)Email capture for development

The dev server runs on the host via tsx watch (not in Docker).

Production Stack

The production Docker Compose (docker/docker-compose.prod.yml) adds Porta as a containerized service:

ServiceImagePortNotes
Portablendsdk/porta:latest3000Multi-stage Alpine build
PostgreSQLpostgres:16-alpine5432With init-test-db.sql for test DB
Redisredis:7-alpine6379Ephemeral (appendonly off)

Admin GUI Service

The Admin GUI runs as a separate Koa BFF process, deployable alongside or independently from the Porta server:

ComponentTechnologyPortPurpose
BFF ServerKoa + koa-session4002OIDC auth, session management, CSRF, API proxy
React SPAReact 19 + FluentUI v9Served by BFFBrowser-based admin dashboard
Session StoreRedis (ioredis)6379 (DB 1)BFF session persistence

Docker deployment: The Admin GUI shares the same Docker image as Porta. Set PORTA_SERVICE=admin to start the BFF instead of the OIDC server. This is configured in the production Docker Compose:

yaml
admin-gui:
  image: blendsdk/porta:latest
  environment:
    PORTA_SERVICE: admin
    PORTA_ADMIN_PORTA_URL: http://porta:3000
    PORTA_ADMIN_CLIENT_ID: <from-porta-init>
    PORTA_ADMIN_CLIENT_SECRET: <from-porta-init>
    PORTA_ADMIN_SESSION_SECRET: <generate-random>
    REDIS_URL: redis://redis:6379/1
  ports:
    - '4002:4002'

Container Build

Multi-Stage Dockerfile

The Dockerfile (docker/Dockerfile) uses a 3-stage multi-architecture build:

Key decisions:

  • Native modules (argon2) require build tools in stages 1 and 2
  • Stage 3 has NO build tools — minimal attack surface
  • Final image runs as non-root user (node:node, UID 1000)
  • Health check: wget --spider http://localhost:3000/health
  • Target size: <200MB

Entrypoint Script

docker/entrypoint.sh handles container startup:

  1. Run database migrations automatically (node dist/lib/migrator.js up)
  2. Start the Porta server (node dist/index.js)

Helper Script

docker/porta.sh provides a convenient wrapper for running CLI commands inside the container:

bash
docker exec -it porta porta <command>

CI/CD Pipeline

GitHub Actions Workflows

Three workflow files in .github/workflows/:

WorkflowTriggerPurpose
build-and-test.ymlPush/PR to mainLint, build, test (unit + integration)
docker.ymlRelease tagsBuild + push Docker image to Docker Hub
docs.ymlPush to main (docs changes)Build + deploy VitePress docs to GitHub Pages

Build & Test Pipeline

Docker Release Pipeline

Network Architecture

Port Mapping

PortServiceProtocol
3000Porta HTTP serverHTTP/1.1
4002Admin GUI BFF serverHTTP/1.1
5432PostgreSQLPostgreSQL wire protocol
6379RedisRESP (Redis Serialization Protocol)
1025MailHog SMTP (dev only)SMTP
8025MailHog UI (dev only)HTTP

TLS Termination

Porta itself serves HTTP on port 3000. TLS termination is handled by a reverse proxy (nginx, Caddy, cloud load balancer) in front of Porta. The ISSUER_BASE_URL configuration must use https:// for production.

Health Monitoring

Health Check Endpoint

GET /health returns the status of both backing services:

json
{
  "status": "healthy",
  "checks": {
    "database": "ok",
    "redis": "ok"
  }
}
  • 200 — Both services healthy
  • 503 — One or more services unhealthy

Docker Health Check

The Dockerfile includes a built-in health check:

dockerfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget --spider --quiet http://localhost:3000/health || exit 1

Backup & Data Persistence

PostgreSQL

  • All persistent data is in PostgreSQL
  • Docker volume porta_postgres_data stores data files
  • Standard pg_dump/pg_restore for backups
  • Migrations are forward-only in production

Redis

  • Redis stores ephemeral data (sessions, cache, rate limit counters)
  • Data loss is tolerable — cache rebuilds from PostgreSQL on miss
  • No persistence configuration needed (appendonly off)

Scaling Considerations

ComponentScaling Strategy
Porta ServerHorizontal (multiple containers behind load balancer)
PostgreSQLVertical (single primary), read replicas possible
RedisVertical (single instance), Redis Cluster for high availability

Stateless server: Porta stores no in-memory state between requests (except the 60-second system config cache). Multiple instances can run behind a load balancer.

Session affinity: Not required. OIDC sessions are stored in Redis, accessible from any Porta instance.

Released under the MIT License.