Openship Image-Registry Container Image Management: Registry Resolution and Drift Detection

Openship treats every container image as a first-class resource by tracking the user-defined image name, resolved registry reference, and immutable SHA256 digest in the service database schema, enabling automated drift detection and registry-agnostic credential lookup.

Openship is an open-source deployment platform that manages containerized applications across multiple environments. The oblien/openship repository implements a robust image-registry container image management system that tracks image provenance from registry resolution through runtime deployment. Understanding how Openship handles container images is essential for operating services securely and detecting configuration drift.

Core Database Schema for Container Images

In packages/db/src/schema/service.ts, the service table defines three critical columns that form the foundation of image tracking:

  • image: The user-provided image name (e.g., postgres:16 or ghcr.io/oblien/openship-api:v3)
  • imageRef: The concrete tag that was actually pulled during deployment
  • imageDigest: The immutable SHA256 content-addressable digest of the running image

According to the source code in packages/db/src/schema/service.ts (lines 89-95), these fields enable Openship to distinguish between the intended image specification and the actual runtime state. When the deployment scanner processes a service, it populates imageRef and imageDigest by inspecting the pulled container image (lines 262-268), then persists this runtime information to enable rollback capabilities and drift detection (lines 251-262).

Registry Resolution and Normalization

Openship resolves ambiguous image references to specific registry hosts using the registryForImage helper function defined in packages/core/src/image-ref.ts (lines 7-20). This function parses image references to extract the registry hostname, handling edge cases such as:

  • Images without explicit registries (defaulting to Docker Hub)
  • Scheme-prefixed hosts (https://ghcr.io)
  • Trailing slashes and normalization

For credential lookup, Openship uses registryConfigKeys (lines 95-103) to generate all possible key variations for a given host. This ensures that credentials match regardless of how the registry was specified, expanding registry.example.com into variants like https://registry.example.com and registry.example.com/.

Deployment Scanning and Digest Tracking

During the deployment lifecycle, Openship's scanner records the exact image state:

  1. The user defines a service with an image specification
  2. The deployment process pulls the image and resolves the full reference (imageRef)
  3. The system computes the SHA256 digest (imageDigest) of the pulled image
  4. Both values are stored in the database alongside the service record

This digest serves as the authoritative proof that the running container matches the scanned version, preventing "dependency confusion" attacks and enabling immutable deployments.

Drift Detection in the Dashboard

The apps/dashboard/src/utils/deploymentPhaseDetector.ts implements logic that compares the stored imageDigest against the latest digest available in the registry. When differences are detected, Openship flags the service as "behind" and surfaces a "swap image" action in the UI. This drift detection mechanism ensures operators can identify services running outdated or modified images compared to their registry counterparts.

CI/CD Image Publishing Pipeline

Openship supports a Docker-only release path defined in scripts/release.ts (lines 156-166) that publishes pre-built images to GitHub Container Registry (GHCR) without modifying Git tags or "latest" tags. This path is executed by the docker-images.yml workflow to push the API, dashboard, and edge images, demonstrating how the platform manages its own container artifacts using the same registry abstractions provided to user services.

Practical Code Examples

Resolving a Registry from an Image Reference

import { registryForImage } from "@openship/core/image-ref";

const ref = "ghcr.io/oblien/openship-api:0.6.5";
const registry = registryForImage(ref);   // → "ghcr.io"

Generating Credential Lookup Keys

import { registryConfigKeys } from "@openship/core/image-ref";

const keys = registryConfigKeys("registry.example.com");
/* keys includes:
   - "registry.example.com"
   - "https://registry.example.com"
   - "registry.example.com/"
*/

Querying Service Image Metadata

import { db } from "@openship/db";
import { eq } from "drizzle-orm";

const service = await db.select()
  .from("service")
  .where(eq("serviceId", "svc-123"))
  .one();

console.log({
  image: service.image,           // user‑provided name
  imageRef: service.imageRef,    // concrete tag pulled
  digest: service.imageDigest,   // SHA256 of the pulled image
});

Summary

  • Triple-field tracking: Openship stores image, imageRef, and imageDigest in packages/db/src/schema/service.ts to maintain complete provenance of container deployments.
  • Registry normalization: The registryForImage and registryConfigKeys utilities in packages/core/src/image-ref.ts handle registry host extraction and credential key generation across various reference formats.
  • Immutable verification: The imageDigest field provides content-addressable verification of running containers, enabling secure rollback and drift detection.
  • CI integration: The scripts/release.ts pipeline demonstrates Docker-only releases to GHCR, separating image publication from version control tagging.

Frequently Asked Questions

How does Openship handle different container registry formats?

Openship normalizes registry hosts by stripping URL schemes, trailing slashes, and lower-casing hostnames using the registryForImage function in packages/core/src/image-ref.ts. The registryConfigKeys utility then generates all possible credential lookup variations, ensuring authentication succeeds regardless of whether users specify https://ghcr.io, ghcr.io/, or ghcr.io.

What is the difference between imageRef and imageDigest in Openship?

The imageRef field stores the concrete tag that was pulled (e.g., ghcr.io/oblien/openship-api:v3), while imageDigest contains the immutable SHA256 hash of that specific image. The digest serves as the authoritative fingerprint for drift detection, allowing Openship to identify when a running container no longer matches the registry's current version of that tag.

How does Openship detect when a service is running an outdated image?

The dashboard's deploymentPhaseDetector.ts compares the imageDigest stored in the database against the latest digest available from the registry. If the digests differ, Openship flags the service as having drift and presents a "swap image" action, enabling operators to update to the latest version or rollback to a previous digest.

Where does Openship store credentials for private container registries?

Registry credentials are managed through the registryConfigKeys abstraction in packages/core/src/image-ref.ts, which expands normalized hostnames into all possible key formats for lookup in Openship's credential store. This allows the system to match credentials against various registry URL formats while keeping the underlying credential storage implementation agnostic to reference formatting.

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 →