MaleusMaleus

Deploy in your infrastructure

Prepare, configure, and validate the Maleus application foundation in your private cloud, data center, or orchestrated platform.

In the appApplication · InfrastructureUpdated September 19, 2026
On this page

You receive the foundation with reference Dockerfiles and a portable runtime contract. Your platform provides the registry, network, secrets, TLS termination, persistent storage, and monitoring. You then connect the integrations specific to your application.

This procedure presents the invariants to preserve, followed by the choices you can adapt to Docker Compose, Kubernetes, OpenShift, ECS, or Nomad.

Understand the reference topology

The nominal runtime brings together three roles:

  1. the frontend serves the SPA, injects its public configuration, and proxies both /api/* and /sso/* to the backend;
  2. the backend exposes the API, applies migrations, and initializes foundation data;
  3. PostgreSQL stores application data.

The browser accesses your application through the frontend. The backend and PostgreSQL remain on the private network. PostgreSQL can run in a container with persistent storage or be replaced by a compatible managed service.

01BrowserThe user's authenticated session
02FrontendSPA, static assets, and proxy for /api and /sso
03BackendAPI, authorization, modules, jobs, and health check
04Data servicesPostgreSQL plus the storage and messaging actually used
One public entry point, separate application services, and explicit dependencies.

The default ports are 3000 for the frontend, 14000 for the backend, and 5432 for PostgreSQL. They may change as long as services, probes, and network rules use the same values. The frontend and backend do not rely on their local filesystems to store business data.

Prepare your platform

Before the first deployment, provide:

  • a registry accessible by your CI and your platform;
  • DNS access and TLS termination;
  • a private network between the frontend, backend, and PostgreSQL;
  • a secret-management mechanism supported by the orchestrator;
  • persistent PostgreSQL storage with tested backup and restore procedures;
  • outbound access required by the integrations that are actually enabled;
  • centralized destinations for logs and, depending on your needs, metrics and traces.

Builds start from the repository root so that the workspace and its lockfile are included. The reference Dockerfiles currently target linux/amd64. If your platform uses another CPU architecture, plan a dedicated build and acceptance test.

The Dockerfiles use the docker/dockerfile:1 BuildKit syntax and COPY --parents. Use Docker with BuildKit or, for an OpenShift BuildConfig, Buildah 1.40 or later. Resolving the syntax image may also require outbound access to the Docker registry during the build.

The backend image runs pnpm commands at startup through Corepack. If the required pnpm version is not already present in the image cache, allow outbound access to registry.npmjs.org or prepare that version in the image before publishing it.

Build and publish immutable images

Build the frontend and backend from the same commit:

docker build -f infra/docker/backend.Dockerfile --platform linux/amd64 \
  -t <registry>/<application>-backend:<version> .

docker build -f infra/docker/frontend.Dockerfile --platform linux/amd64 \
  -t <registry>/<application>-frontend:<version> .

Publish an immutable tag or digest, then record both references in the acceptance evidence. No secret should be injected at build time or remain in an image layer. Keep MOCK_API=false for the frontend build so your application uses the real API.

The official PostgreSQL image is sufficient when your migrations only use extensions available on your platform. The foundation's PostgreSQL Dockerfile can derive an image when your project requires an additional extension. Record the version and extensions in your compatibility matrix.

Configure without exposing secrets

Inject configuration at runtime. Keep secret values in your platform's secret mechanism and never commit them.

PostgreSQL container

VariablePurpose
POSTGRES_USERuser created by the PostgreSQL container
POSTGRES_DBdatabase created on first startup
POSTGRES_PASSWORDpassword used by bootstrap
PGDATAdata location on persistent storage

Backend

VariablePurpose
APP_ENVIRONMENTapplication environment
POSTGRES_HOST, POSTGRES_PORTPostgreSQL address
POSTGRES_DB_NAME, POSTGRES_DB_USERapplication database and user
POSTGRES_PASSWORDPostgreSQL bootstrap authentication
POSTGRES_DB_USER_PASSWORDapplication runtime authentication
JWT_SECRET, COOKIE_SECRET, PASSWORD_PEPPERsession, cookie, and password signing material
BACKEND_URLcomplete public URL of your application, used in particular by SSO

The backend also accepts conditional or optional variables:

  • DEFAULT_USER_PASSWORD for initial bootstrap;
  • FRONTEND_URL for SSO redirects;
  • PORT, defaulting to 14000;
  • POSTGRES_SSL and POSTGRES_SSL_CA for PostgreSQL over TLS;
  • NODE_EXTRA_CA_CERTS for a private PKI, including an IdP's CA;
  • CORS_ALLOWED_ORIGINS for a genuinely cross-origin topology;
  • COOKIE_DOMAIN to explicitly share a session across multiple hosts;
  • WORKSPACE_SSO_ENABLED for workspace-specific SSO providers;
  • CRON_ENABLED, CRON_INSTANCE_ID, and CRON_SHUTDOWN_DRAIN_MS for scheduled tasks;
  • LOG_LEVEL for logging verbosity;
  • HTTPS_PROXY and NO_PROXY when Corepack reaches the package registry through a corporate proxy;
  • COREPACK_HOME when your platform enforces an arbitrary UID;
  • OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, OTEL_TRACES_EXPORTER, and OTEL_SDK_DISABLED for OpenTelemetry.

DATABASE_URL can configure the application pool, but does not replace the separate PostgreSQL variables in the current image: startup bootstrap still consumes them.

OpenTelemetry remains disabled until an exporter is configured. OTEL_EXPORTER_OTLP_ENDPOINT enables export to the collector, OTEL_SERVICE_NAME identifies your application in traces, OTEL_TRACES_EXPORTER=console supports local checks, and OTEL_SDK_DISABLED=true forces instrumentation off.

In the current backend image, PostgreSQL bootstrap authenticates with POSTGRES_DB_USER and POSTGRES_PASSWORD, while the application runtime uses POSTGRES_DB_USER and POSTGRES_DB_USER_PASSWORD. With the nominal PostgreSQL container, these values generally identify the same account and secret. Strict separation between an administrative account and the application account requires adapting the bootstrap job or entrypoint.

Frontend and Maleus SDK

VariablePurpose
BACKEND_URLprivate target for the /api/* and /sso/* proxies
PROJECT_NAMEname displayed by your application
BACKEND_API_URLAPI prefix used by the browser
BASE_PATHoptional path prefix
PORTfrontend server port, defaulting to 3000
FRAME_ANCESTORSorigins allowed to embed your application
MALEUS_APP_API_KEYlimited-scope application key that enables the SDK
MALEUS_API_PUBLIC_URL, MALEUS_FRONTEND_PUBLIC_URLpublic SDK origins
MALEUS_PROJECT_ID, MALEUS_ENVIRONMENTassociation of feedback with the project and environment
MALEUS_SDK_CONFIGJSON object that enables SDK features

The Maleus SDK is optional and disabled while MALEUS_APP_API_KEY is empty. This key is a sensitive, limited-scope configuration value, but it is not a server-side secret: when enabled, the frontend injects it into maleus-config.js, where the browser can read it. It must not grant any administrative privilege or access to application data.

MALEUS_SDK_CONFIG contains a valid JSON object. A missing or invalid value applies a fail-closed configuration that disables the widget and its features. When the SDK is enabled, browsers must be able to reach the origins configured by MALEUS_FRONTEND_PUBLIC_URL and MALEUS_API_PUBLIC_URL over HTTPS.

This list covers the current foundation. Variables introduced by generated features, such as object storage, SMTP, business APIs, or other integrations, are added from the delivered repository's code and configuration contract.

Configure SSO

Configure OIDC or SAML providers in the administration interface. The foundation stores them in the database. Their issuer, client ID, client secret, certificates, and mappings are not foundation environment variables.

On the backend, set BACKEND_URL and FRONTEND_URL to your application's complete public URL, including any path prefix. Login and callback routes use the /sso/... path under the public URL and pass through the frontend's /sso/* proxy. The backend remains private.

If the IdP uses a private certificate authority, mount the PEM CA chain in the backend and point NODE_EXTRA_CA_CERTS to that file. WORKSPACE_SSO_ENABLED=false disables workspace-specific providers.

Orchestrate startup and migrations

The first backend follows an ordered sequence:

  1. wait until PostgreSQL accepts connections;
  2. idempotently create the required roles, database, schemas, and extensions;
  3. apply migrations;
  4. idempotently create foundation fixtures;
  5. start the Node process and expose the health check.

The frontend receives traffic once the backend is ready. The base model starts a single backend against a given database while migrations run. For multiple replicas, run migrations in an exclusive job or separate bootstrap from the server before allowing parallel deployment.

Before a sensitive migration, produce a restorable backup. A restore rehearsal validates the procedure, recovery time, and required permissions.

Adapt the contract to the orchestrator

Docker Compose

The deployment file contains a named PostgreSQL volume, a pg_isready health check, a backend that depends on a healthy database, and a frontend that depends on the backend. Only the frontend port is published.

Inject secrets as environment variables through your platform's secret mechanism, or from a Git-ignored local file for Compose validation. Docker secrets mounted as files require an adapter entrypoint because the current images do not automatically read *_FILE variants.

Add a restart policy to the services. depends_on orders initial startup but does not guarantee that a restarted backend waits for PostgreSQL. The backend exits when bootstrap fails and must then be restarted. Validate the resolved configuration, check service status, and inspect logs. A second startup must remain idempotent.

Kubernetes and OpenShift

Deploy the frontend and backend as separate Deployment resources with private Service resources. The Ingress or Route targets only the frontend. PostgreSQL uses a managed service or a StatefulSet with persistent storage.

Distinguish startupProbe, readinessProbe, and livenessProbe. While migrations remain part of backend startup, keep one replica or run an exclusive migration job. On OpenShift, also validate the SCC policy, runtime UID, read-only filesystem, and volume permissions. If your platform assigns an arbitrary UID, set COREPACK_HOME=/tmp/corepack and keep /tmp writable.

ECS or Nomad

On ECS, the frontend and backend can share a task and communicate through localhost, or become separate services connected by the private network. The ALB targets the frontend. Secrets come from your platform's secret manager and logs are centralized.

On Nomad, group tasks that communicate over loopback or declare private services. Checks drive restarts and traffic removal. PostgreSQL storage comes from CSI or a managed database. The update strategy prevents two migrations from running simultaneously.

Complete TLS, persistence, and health checks

The reverse proxy or load balancer terminates TLS, redirects HTTP to HTTPS, and applies HSTS. It forwards the original host and protocol correctly. /api/* and /sso/* enter through the frontend, which forwards requests to the private backend. The browser does not connect directly to the backend.

To serve your application under /myapp, set BASE_PATH=/myapp and BACKEND_API_URL=/myapp/api on the frontend, then set BACKEND_URL=https://application.example/myapp and, when SSO is enabled, FRONTEND_URL=https://application.example/myapp on the backend. Route the entire /myapp prefix to the frontend without URL rewriting. The frontend serves the SPA and proxies /myapp/api/* and /myapp/sso/*. The same frontend image can be reused without rebuilding it.

The reference health checks are:

  • PostgreSQL: pg_isready;
  • private backend: GET /server/health-check;
  • backend through the public entry point: GET /api/server/health-check;
  • frontend: the root path or the root defined by BASE_PATH.

The backend health check returns 200 when the dependencies it checks are healthy and 503 otherwise. Startup delays cover migration time without causing a restart loop.

PostgreSQL carries the persistence of the nominal foundation. The operating policy defines the RPO, RTO, retention, encryption, backup monitoring, and restore-test frequency. Each storage service added by the project follows its own policy.