How Backend Services Are Managed in Openship: Architecture and Lifecycle

Openship treats every deployable unit as a database-backed service entity, supporting both Docker Compose and monorepo architectures while automating routing, drift detection, and deployment state tracking.

Openship provides a comprehensive system for managing backend services within deployment projects. The platform abstracts every deployable unit—whether a Docker Compose entry or a monorepo sub-application—as a service backed by a centralized PostgreSQL schema. This architecture enables consistent handling of routing configuration, environment variables, and deployment lifecycles across diverse project types.

Service Types and Database Schema

Openship recognizes two distinct service kinds defined in the database schema at packages/db/src/schema/service.ts:

  • kind = "compose" – Represents traditional Docker Compose services with fields for image, build context, and port mappings.
  • kind = "monorepo" – Represents sub-applications within a monorepo, storing rootDirectory, install commands, build commands, start commands, and framework metadata.

The service table serves as the single source of truth, capturing both infrastructure-agnostic metadata and runtime-specific configuration. Public routing is handled through scalar columns (exposed, exposedPort, domain, customDomain, domainType) alongside a JSONB publicEndpoints array. This structure allows a single service exposing multiple ports to maintain distinct routing rules for each endpoint.

CRUD Operations and Business Logic

The core service management logic resides in apps/api/src/modules/services/service.service.ts. This module exposes several key operations:

  • createService and updateService – Validate incoming data through normalizeRoutingPatch to ensure routing consistency, enforce custom hostname rules via isValidCustomHostname, and persist changes through the repos.service data-access layer.
  • listServices and getService – Retrieve service rows while enriching the response with any pending drift detected from upstream compose file changes.

All routing mutations trigger domain record updates through helper functions that interface with the DNS and certificate management systems.

Drift Management for Compose Services

When a project's docker-compose.yml file changes, Openship calculates a drift between the stored baseline (importedSpec) and the newly parsed specification. The system stores this diff in the driftSpec column, presenting users with two resolution paths:

  1. acceptServiceDrift – Merges the pending changes into the service row, updating fields such as image references, port mappings, and environment variables to match the compose file.
  2. keepServiceDrift – Discards the detected drift, maintaining the current baseline configuration despite changes in the repository.

This mechanism ensures that manual overrides made through the Openship interface are not silently overwritten by repository updates, while still providing a clear path to synchronize with upstream changes.

Routing and Domain Configuration

Routing orchestration is delegated to apps/api/src/lib/routing-domains.ts. This library synthesizes OpenResty edge configurations through two primary functions:

  • buildServiceRouteDomain – Generates the vhost configuration for a single service endpoint.
  • buildServiceRouteDomains – Processes the publicEndpoints array to create multiple route definitions for services with several exposed ports.

After service creation or routing field modifications, the service layer invokes ensurePendingServiceDomain to provision DNS records and TLS certificates, or removeServiceDomain to tear them down. These operations execute with a ROUTE_EDGE_APPLY_TIMEOUT_MS of 6000 milliseconds to prevent slow remote edge updates from blocking API responses.

Deployment Pipeline and State Tracking

Per-service deployment status is maintained in the service_deployment table, defined alongside the main service schema. The deployment flow follows this sequence:

  1. The deployment controller at apps/api/src/modules/deployments/deployment.controller.ts initiates a build session.
  2. The build service (apps/api/src/modules/deployments/build.service.ts) iterates over enabled services, creating a ServiceHandle via buildServiceHandle (referenced in backup.orchestrator.ts and restore.orchestrator.ts).
  3. The system selects the appropriate runtime—Docker for compose services or bare execution for monorepo apps—based on the service's kind field.
  4. Upon completion, the service_deployment row updates with final status values (success, failed, or skipped) alongside timestamps (startedAt, finishedAt).

Service Lifecycle and Cleanup

Services can be temporarily excluded from deployments without database deletion by setting enabled = false. Permanent removal triggers the project cleanup service at apps/api/src/modules/projects/project-cleanup.service.ts, which invokes buildServiceRouteDomain to purge routing configurations and associated resources.

The system also enforces plan limitations through assertFreeEndpointsAllowed, preventing accidental exposure of free sub-domains when the organization's subscription does not permit them.

Summary

  • Openship unifies Docker Compose and monorepo workflows under a single service abstraction stored in PostgreSQL.
  • The service.service.ts module handles CRUD operations, routing validation, and drift management for repository-driven configuration changes.
  • Drift detection allows users to accept or reject changes originating from modified docker-compose.yml files.
  • Routing configuration is generated via routing-domains.ts and applied with a 6-second timeout to edge proxies.
  • Deployment state is tracked per-service in the service_deployment table, with builds orchestrated through the deployment controller and build service.

Frequently Asked Questions

What is the difference between compose and monorepo services in Openship?

Compose services map directly to Docker Compose entries, utilizing fields like image, build context, and Docker-specific port syntax. Monorepo services represent sub-applications within a larger repository, storing framework metadata and command scripts (installCommand, buildCommand, startCommand) alongside a rootDirectory path. Both types share the same routing and deployment infrastructure but execute through different runtimes during the build phase.

How does Openship handle changes to docker-compose.yml files?

When Openship detects modifications to a project's docker-compose.yml, it parses the new specification and compares it against the stored importedSpec baseline. The resulting difference is stored as a drift in the driftSpec column. Users must explicitly choose to acceptServiceDrift (applying the changes to the service configuration) or keepServiceDrift (preserving the current settings), preventing automatic overwrites of manual configuration adjustments.

What happens when a service is deployed in Openship?

The deployment controller triggers the build service, which creates a ServiceHandle for each enabled service and selects the appropriate runtime based on the kind field. The system executes the build process, streams logs to the client, and updates the service_deployment table with the final status and timestamps. If the service is exposed publicly, the routing library simultaneously provisions DNS records and TLS certificates through the domain management helpers.

How does Openship manage custom domains and SSL certificates?

Custom domains are validated through isValidCustomHostname and stored in the customDomain column. The ensurePendingServiceDomain function provisions the necessary DNS records and TLS certificates, while buildServiceRouteDomain generates the corresponding OpenResty configuration. When services are deleted or routing is disabled, removeServiceDomain tears down these resources to prevent orphaned certificates and DNS entries.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →