Deployments

Deploy with Docker

Pull, verify, run, upgrade, and roll back the dcmage browser container.

Browser only

Use latest for an initial deployment. For production, select a published version from the GitHub container package. Use the version tag or its digest as the permanent deployment reference.

Pull and inspect the image

export DCMAGE_IMAGE=ghcr.io/kiwiprojekt/dcmage-release:latest
docker pull "$DCMAGE_IMAGE"
docker buildx imagetools inspect "$DCMAGE_IMAGE"

The manifest lists the AMD64 and ARM64 variants and the published digest. BuildKit attaches provenance and an SBOM to the image. The application also serves its CycloneDX SBOM at /sbom.cyclonedx.json.

Before production use, open the GitHub container package page. Copy the newest version tag. Replace latest in DCMAGE_IMAGE with that tag. Pull and inspect the versioned image before deployment.

Run with Docker

Use HTTPS for remote access

Browsers can disable security-sensitive features on remote HTTP pages.

docker run --detach \
  --name dcmage \
  --publish 127.0.0.1:8080:8080 \
  --read-only \
  --tmpfs /tmp:size=16m,mode=1777 \
  --tmpfs /var/cache/nginx:size=16m,uid=101,gid=101 \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  --restart unless-stopped \
  "$DCMAGE_IMAGE"

curl --fail http://127.0.0.1:8080/healthz

Open http://127.0.0.1:8080 in a supported browser.

Run with Docker Compose

Save this as compose.yaml:

services:
  dcmage:
    image: ghcr.io/kiwiprojekt/dcmage-release:latest
    ports:
      - '127.0.0.1:8080:8080'
    read_only: true
    tmpfs:
      - /tmp:size=16m,mode=1777
      - /var/cache/nginx:size=16m,uid=101,gid=101
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    restart: unless-stopped

Start the service and check its health:

docker compose up --detach
docker compose ps
curl --fail http://127.0.0.1:8080/healthz

Put it behind a reverse proxy

The application needs normal HTTP GET and HEAD requests. It does not need a WebSocket or upload-body route. The proxy must preserve the correct MIME types for JavaScript, WebAssembly, JSON, and gzip data.

The following NGINX example terminates TLS. Replace the server name and certificate paths.

server {
    listen 443 ssl http2;
    server_name dcmage.example.org;

    ssl_certificate /etc/letsencrypt/live/dcmage.example.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dcmage.example.org/privkey.pem;

    client_max_body_size 1m;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

The image supports root-path hosting only. Do not add or remove a URL prefix. Limit port 8080 to the proxy host or private network.

Upgrade and roll back

  1. Record the current image digest.
  2. Pull the new versioned image.
  3. Inspect its digest, provenance, and SBOM.
  4. Replace the container with the new digest.
  5. Check /healthz and open a local DICOM file.
  6. Restore the recorded digest if verification fails.

The container has no application database or DICOM volume. Back up only the deployment configuration and pinned digest.

Air-gapped deployment

Move the pinned image with docker save and docker load. The image already contains fonts, dictionaries, codecs, OCR code, and OCR language data. The browser needs access to the local container. It does not need internet access for application assets.

See Docker troubleshooting when the container starts but the application does not work as expected.