Da
Storage Azure

dcm4chee-arc-light on Ubuntu 24.04 on Azure User Guide

| Product: dcm4chee-arc-light on Ubuntu 24.04 LTS on Azure

Overview

This image runs dcm4chee-arc-light 5.34.3 on Ubuntu 24.04 LTS. dcm4chee-arc-light is a production grade DICOM archive, the kind of picture archiving and communication system (PACS) a radiology department runs at the centre of its imaging. Scanners, ultrasound machines and other modalities speak DICOM, and dcm4chee gives them somewhere to send studies: it listens on the DICOM protocol, indexes every patient, study, series and instance it receives into PostgreSQL, and makes them browsable in its built in web interface, UI2. The same archive is exposed through the DICOMweb REST APIs (QIDO-RS to query, WADO-RS to retrieve, STOW-RS to store) so imaging can be pulled into research pipelines, teaching archives, reporting tools or your own applications.

No credential ships in this image, and it is locked until first boot. The upstream dcm4chee compose stack is widely documented with well known defaults (admin / admin for the web tier, pacs for PostgreSQL, secret for the LDAP and keystore passwords). None of them exist in this image. On your VM's first boot, dcm4chee-arc-firstboot.service generates a fresh random value for every one of those secrets from the kernel random number generator, and generates a unique web password for you. Until that runs the archive does not start at all, and the front door is fail closed: every request to the UI or the API answers 401 rather than serving your archive openly.

What is included:

  • dcm4chee-arc-light 5.34.3, run as the upstream minimal three container stack, pinned by digest
  • The archive application server (WildFly hosting the dcm4chee-arc EAR + the UI2 web app)
  • Genuine PostgreSQL for the index and OpenLDAP for the dcm4che device configuration
  • The UI2 web interface, served through nginx on port 80 behind HTTP Basic authentication
  • The DICOM server (SCP) listening on port 11112 with AE title DCM4CHEE
  • The DICOMweb REST APIs (QIDO-RS / WADO-RS / STOW-RS) on port 80, behind the same authentication
  • A unique web password generated on this VM at first boot, in a root only file
  • dcmtk, the standard DICOM toolkit (echoscu, storescu, findscu, dcmdump) for testing
  • A synthetic phantom study you can send in one command to prove the install end to end
  • A dedicated Azure data disk for all imaging, index and configuration state
  • The licence and a written offer shipped on the image at /usr/share/doc/dcm4chee-arc-light/

Why an nginx authentication gate. The minimal dcm4chee stack has no authentication of its own; access control in the full product is a Keycloak secured variant that needs far more memory. Rather than expose the archive openly, this image puts an nginx HTTP Basic authentication gate in front of the entire UI2, REST and DICOMweb surface, with a per VM password generated at first boot. An unauthenticated request returns 401, the well known admin / admin is rejected, and only your per VM credential is served. The one deliberate exception is /healthz, an unauthenticated static endpoint nginx serves for the Azure Load Balancer probe.

Prerequisites

  • An Azure subscription
  • The Azure CLI if you deploy from the command line
  • An SSH key pair
  • Standard_B2ms (2 vCPU, 8 GB) is the recommended floor. The stack idles around 1.5 to 2 GB, but the WildFly application server, the bundled transcoding and concurrent ingest need real headroom. Use a larger D series VM and a larger data disk for a production PACS workload.

Step 1 - Deploy from the Azure Marketplace

Search the Azure Marketplace for dcm4chee-arc-light DICOM Archive on Ubuntu 24.04 LTS by cloudimg, select Create, and work through the wizard. Choose SSH public key authentication with the username azureuser.

On the Networking tab, allow inbound SSH (22) and HTTP (80). You will also need 11112 for DICOM, but read Step 9 before opening it to the internet.

Step 2 - Deploy from the Azure CLI

az group create --name dcm4chee-rg --location eastus

az vm create \
  --resource-group dcm4chee-rg \
  --name dcm4chee-vm \
  --image cloudimg:dcm4chee-arc-light-ubuntu-24-04:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group dcm4chee-rg --name dcm4chee-vm --port 80 --priority 1001

# Accept the image terms once per subscription:
az vm image terms accept --urn cloudimg:dcm4chee-arc-light-ubuntu-24-04:default:latest

The archive, its index and its configuration all live on a dedicated data disk that is provisioned and mounted at /var/lib/dcm4chee automatically.

Step 3 - Connect to your VM

ssh azureuser@<vm-ip>

Step 4 - Confirm the services are running

The archive runs as three upstream containers under Docker Compose, wrapped by three systemd units. dcm4chee-arc-firstboot.service generates your per VM secrets on the first boot, dcm4chee-arc.service brings the container stack up, and nginx.service is the authenticated front door on port 80.

systemctl is-active docker dcm4chee-arc dcm4chee-arc-firstboot nginx

All four report active:

active
active
active
active

You can see the three containers directly:

sudo docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}'
dcm4chee-arc-1     dcm4che/dcm4chee-arc-psql:5.34.3      Up 2 minutes
dcm4chee-db-1      dcm4che/postgres-dcm4chee:17.4-34     Up 2 minutes
dcm4chee-ldap-1    dcm4che/slapd-dcm4chee:2.6.10-34.3    Up 2 minutes

Only three ports are open to the network: SSH, nginx on 80 (and 443), and the DICOM server on 11112. WildFly's own 8080 is bound to loopback only, because nginx is what serves it, and PostgreSQL and OpenLDAP are not exposed off the host at all.

sudo ss -ltnp | grep -E ':80 |:443 |:11112 |:8080 '
LISTEN 0.0.0.0:80        users:(("nginx",...))
LISTEN 0.0.0.0:443       users:(("nginx",...))
LISTEN 127.0.0.1:8080    users:(("docker-proxy",...))
LISTEN 0.0.0.0:11112     users:(("docker-proxy",...))

The docker, dcm4chee-arc, dcm4chee-arc-firstboot and nginx units all reporting active, the three archive containers running, and the listening ports showing nginx on 80 and the DICOM server on 11112 while WildFly 8080 stays on loopback

Step 5 - Retrieve your web password

Your password was generated on this VM at first boot and written to a root only file:

sudo cat /root/dcm4chee-arc-light-credentials.txt

The file records your URL, username and password, and warns you about the DICOM port:

DCM4CHEE_URL=http://<vm-ip>/dcm4chee-arc/ui2/
DCM4CHEE_WEB_USERNAME=dcm4chee
DCM4CHEE_UI_PASSWORD=<your unique password>

The same credential authenticates the UI2 web interface, the REST API and the DICOMweb endpoints. It is generated per VM, so no two deployments of this image share a password.

Step 6 - Open the UI2 web interface

Browse to http://<vm-ip>/ (which redirects to /dcm4chee-arc/ui2/) and sign in with the username dcm4chee and the password from Step 5. UI2 opens on the Studies page with its query form and the navigation across Patients, Studies, Series, MWL and Work Items. Press SUBMIT to list what is in the archive; a fresh archive is empty until something is sent to it, which is Step 7.

The dcm4chee UI2 web interface open on the Studies page after signing in with the per VM credential, showing the study query form with its patient, modality and date filters, the SUBMIT button and the Patients, Studies and Series navigation

Step 7 - Send your first study over DICOM

The image ships a small synthetic phantom study at /usr/share/dcm4chee-arc-light/sample-dicom/ so you can prove your archive works end to end immediately. It is procedurally generated geometry and contains no patient data of any kind.

Connect to the VM (Step 3) and run these from there. First check that the DICOM server answers a C-ECHO, the DICOM equivalent of a ping. DICOM peers identify each other by AE title: your calling AE title is SMOKESCU, the archive's is DCM4CHEE.

echoscu -aet SMOKESCU -aec DCM4CHEE 127.0.0.1 11112 && echo "C-ECHO: association accepted"
C-ECHO: association accepted

Now send the study with a real C-STORE, exactly as a modality would (a modality on your network would target the VM's own IP on 11112 instead of loopback):

storescu -aet SMOKESCU -aec DCM4CHEE 127.0.0.1 11112 /usr/share/dcm4chee-arc-light/sample-dicom/*.dcm

Confirm the archive persisted and indexed it by querying it back with a real C-FIND at study level:

findscu -S -aet SMOKESCU -aec DCM4CHEE 127.0.0.1 11112 \
  -k QueryRetrieveLevel=STUDY -k StudyInstanceUID -k PatientName -k StudyDate

The response carries your stored study, which proves the object was not merely accepted but persisted and made queryable:

...
(0010,0010) PN [PHANTOM^CLOUDIMG]                       #  16, 1 PatientName
(0020,000d) UI [1.2.826.0.1.3680043.8....]              #  ..., 1 StudyInstanceUID
...

A DICOM C-ECHO association accepted, a C-STORE sending the phantom study to AE title DCM4CHEE on port 11112, and a C-FIND query returning the stored study by StudyInstanceUID and PatientName

Back in UI2, press SUBMIT on the Studies page and the stored study is there, listed under the phantom patient with its study date, modality and the number of instances it holds.

The dcm4chee UI2 Studies page after querying, listing the stored phantom study under patient PHANTOM CLOUDIMG with its study date, modality and instance count

Expand the study to see the two series that arrived, each with its description, modality and instance count.

The dcm4chee UI2 view of the phantom study expanded to show its two CT series, an axial phantom series of 8 instances and a sagittal reformat series of 4 instances

Expand a series to see every instance the modality sent, each stored with its SOP class and image dimensions.

The dcm4chee UI2 view of a series expanded to its instances, showing a CT Image Storage instance with its 256x256 16 bit image dimensions

Step 8 - Query over DICOMweb

Everything UI2 shows is also available over the DICOMweb REST APIs, behind the same authentication. Query studies with QIDO-RS:

PW=$(sudo sed -n 's/^DCM4CHEE_UI_PASSWORD=//p' /root/dcm4chee-arc-light-credentials.txt)
curl -s -u dcm4chee:$PW \
  "http://127.0.0.1/dcm4chee-arc/aets/DCM4CHEE/rs/studies" | head -c 400; echo

QIDO-RS returns the studies as DICOM JSON, keyed by tag. 00100010 is the patient name, 0020000D the study instance UID:

[{"00080005":{"vr":"CS","Value":["ISO_IR 100"]},"00080020":{"vr":"DA","Value":["20260716"]},
 "00100010":{"vr":"PN","Value":[{"Alphabetic":"PHANTOM^CLOUDIMG"}]},
 "0020000D":{"vr":"UI","Value":["1.2.826.0.1.3680043.8..."]}, ...}]

The management interface for the archive lives under the same authenticated path, so the whole DICOMweb and dcm4chee REST API is available to your own applications with the one credential.

Step 9 - Confirm the security posture, and restrict the DICOM port

Everything on port 80 requires your password. An unauthenticated request to the archive is refused:

curl -s -o /dev/null -w 'unauthenticated: HTTP %{http_code}\n' \
  http://127.0.0.1/dcm4chee-arc/aets/DCM4CHEE/rs/studies
unauthenticated: HTTP 401

The well known default credential does not work either, because it does not exist on this image:

curl -s -o /dev/null -w 'default admin/admin: HTTP %{http_code}\n' \
  -u admin:admin http://127.0.0.1/dcm4chee-arc/aets/DCM4CHEE/rs/studies
default admin/admin: HTTP 401

The one endpoint that answers without a credential is the load balancer health probe, which exposes no archive data:

curl -s http://127.0.0.1/healthz
ok

The DICOM port has no password. This is how the DICOM protocol works, not a choice this image made: DICOM identifies peers by AE title, not by password, so anyone who can reach port 11112 can send images to your archive. Treat network reachability as the access control. Restrict 11112 to your modalities:

az network nsg rule create \
  --resource-group dcm4chee-rg \
  --nsg-name dcm4chee-vmNSG \
  --name allow-dicom-from-modalities \
  --priority 1002 \
  --source-address-prefixes <modality-ip>/32 \
  --destination-port-ranges 11112 \
  --access Allow --protocol Tcp

An unauthenticated DICOMweb request refused with HTTP 401, the well known admin admin default also refused with 401, and the unauthenticated healthz endpoint returning ok for the Azure load balancer probe

Step 10 - Put TLS in front of the archive

nginx uses HTTP Basic authentication, which sends your password on every request. Do not use it across an untrusted network without TLS. Point a DNS name at the VM, then:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d dcm4chee.example.com

Certbot rewrites the nginx site in place and renews automatically. The archive configuration does not change.

Step 11 - Licence and provenance

dcm4chee-arc-light is free software under the Mozilla Public License Version 1.1 (MPL-1.1). The licence notice, the recorded build provenance and a written offer ship on the image:

ls /usr/share/doc/dcm4chee-arc-light/
BUILD-INFO.txt  LICENCE-NOTICE.txt  WRITTEN-OFFER.txt

BUILD-INFO.txt records the exact upstream container images and their resolved digests, so the build is reproducible and unmodified:

head -12 /usr/share/doc/dcm4chee-arc-light/BUILD-INFO.txt
dcm4chee-arc-light 5.34.3 — cloudimg Azure Marketplace image
Built:           2026-07-20T...Z
Topology:        3 upstream containers under docker compose (ldap + postgres + arc)
Patches applied: none (unmodified upstream images, pinned by tag)
Licence:         Mozilla Public License (detected version: 1.1)

Pinned images and resolved digests:
  dcm4che/slapd-dcm4chee:2.6.10-34.3
  dcm4che/postgres-dcm4chee:17.4-34
  dcm4che/dcm4chee-arc-psql:5.34.3

cloudimg applies no patches to dcm4chee: what you run is the unmodified upstream release, pinned by digest. Nothing in this image or its support terms restricts the rights the licence grants you over dcm4chee itself.

The dcm4chee-arc-light licence notice, written offer and recorded build provenance shipped on the image, showing the pinned upstream container images and their resolved digests

Maintenance

Where your imaging lives. The DICOM objects, the PostgreSQL index and the OpenLDAP configuration are all under the dedicated data disk mounted at /var/lib/dcm4chee (in arc-storage, db and ldap). Back that volume up as a unit, with the stack stopped:

sudo systemctl stop dcm4chee-arc
sudo tar czf /tmp/dcm4chee-backup.tar.gz -C /var/lib/dcm4chee .
sudo systemctl start dcm4chee-arc

For a production archive, attach a larger Azure data disk when you deploy and it is mounted at /var/lib/dcm4chee automatically, keeping all imaging off the OS disk.

Configuration. The dcm4che device configuration lives in OpenLDAP, editable through UI2 under the device and archive settings pages, or over the REST configuration API. The per VM secrets live in /var/lib/dcm4chee/.env.

Logs.

sudo docker compose -f /opt/dcm4chee/compose.yaml logs --tail 50 arc

Updates. The base OS takes Ubuntu security updates automatically through unattended-upgrades. The dcm4chee containers are pinned to digest; upgrade them deliberately by bumping the pinned tags when a new upstream release is validated.

Support

Commercial support is included with this image. Contact support@cloudimg.co.uk.