dcm4chee-arc-light 5.35.2 on AWS User Guide
Overview
This guide covers launching and using the dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg file sharing and sync listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
dcm4chee-arc-light is an open source DICOM archive (PACS archive). It stores medical images sent to it over DICOM (C-STORE) or DICOMweb (STOW-RS), indexes them in PostgreSQL, and serves them back over DICOM query/retrieve and DICOMweb (QIDO-RS, WADO-RS, WADO-URI). Its UI2 web interface lets you search studies, look at series and instances, and configure the archive. Upstream ships it only as container images; this AMI runs the upstream stack (an OpenLDAP configuration store, PostgreSQL and the WildFly based archive) under Docker Compose and adds the security work below.
- HTTPS from the first boot. nginx serves the archive on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped.
- A sign in unique to your instance. Upstream's "light" archive has no authentication on its web UI, REST API or DICOMweb. On this image nginx asks for a user name and password on every web path; the password is generated on first boot and written to
/root/dcm4chee-arc-light-credentials.txt(root only). - No upstream default password is ever live. Upstream defaults (LDAP
secret, PostgreSQLpacs/pacs, the WildFly keystoresecret, LDAP users with the passwordchangeit) are replaced on first boot by values generated on your instance, the LDAP and PostgreSQL databases are created on first boot, and the default LDAP users are not created at all. The archive's own TLS key pair is generated on your instance too; upstream ships one key shared by every copy of its image. - DICOM DIMSE stays on localhost until you decide. DICOM associations carry no password: the archive accepts any calling AE title unless you configure an allowlist (upstream advisory GHSA-q39p-rchv-x498, which has no fix in any release). Port 11112 is therefore published on
127.0.0.1only, anddcm4chee-dimse openrefuses to publish it until you have set the AE titles of your modalities (Step 8). HL7 (MLLP 2575/12575, advisory GHSA-q47j-x5mg-ff23) is never published. - Nothing else on the network. Only SSH (22) and nginx (80, 443) listen on the instance's addresses. PostgreSQL, LDAP, WildFly management and the archive's own HTTP are reachable from the containers' private network only, and from the instance itself only by root (and, for the archive's HTTP, nginx).
- No container can reach the metadata service. The archive, LDAP and PostgreSQL containers are blocked from the EC2 instance metadata service, and the AMI requires IMDSv2.
- Cross site protection. Another web site cannot make your signed in browser change the archive, and the routes that make the server contact other systems (DIMSE queries to other archives, patient demographics queries, host lookups) need proof that the request comes from your own pages.
- Pinned, verified components. Every container image is pinned by digest. PostgreSQL runs 18.6 (upstream's own database image is PostgreSQL 18.3, without the 18.4 and 18.6 security fixes) with the archive's upstream schema scripts.
- Your data on its own volume. LDAP configuration, the PostgreSQL index and the DICOM objects live on a dedicated 32 GiB EBS volume mounted at
/var/lib/dcm4chee, which you can resize independently. - Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: dcm4chee-arc-light 5.35.2 (WildFly 41, Amazon Corretto 21), OpenLDAP 2.6, PostgreSQL 18.6, Docker Engine 29 with the Compose plugin, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. A small synthetic test study (a geometric phantom, no patient data) is in /usr/share/dcm4chee-arc-light/sample-dicom.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large (larger for busy archives)
Clinical use: this image is infrastructure only. You remain responsible for the controls your regulations require (for example HIPAA or GDPR): access control, encryption of data at rest (enable EBS encryption for your volumes), network isolation, audit and backups. It is not certified for any regulatory scheme.
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (the archive) and TCP 80 (redirects to HTTPS) from your own networks only. Do not open 11112 until Step 8.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger), your key pair, a security group as above, and a subnet. Under Configure storage you will see two volumes: the root volume and the 32 GiB data volume for the archive; raise the data volume's size now if you already know you need more.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=dcm4chee}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot generates every password, key and certificate, creates the LDAP and PostgreSQL databases and starts the archive. It takes about a minute:
systemctl is-active docker cloudimg-dcm4chee-netguard dcm4chee-arc-firstboot dcm4chee-arc nginx
active
active
active
active
active
The three containers run under Docker Compose:
sudo docker compose -f /opt/dcm4chee/compose.yaml ps --format '{{.Service}} {{.State}}'
arc running
db running
ldap running
Only SSH (22) and nginx (80, 443) listen on the network. The archive's HTTP (8080) and DICOM (11112) ports are on loopback only, and the other two are Ubuntu's local DNS resolver:
sudo ss -lntp | awk 'NR>1 {print $4}' | sort -u
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:11112
127.0.0.1:8080
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80
If nginx is not active, first boot has not finished (or failed, in which case nginx stays down). Check systemctl status dcm4chee-arc-firstboot and journalctl -u dcm4chee-arc-firstboot.
Step 4: Get your URL and password
sudo cat /root/dcm4chee-arc-light-credentials.txt
The file is readable by root only:
# dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 (cloudimg). Generated ON THIS INSTANCE at first boot.
DCM4CHEE_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/dcm4chee-arc/ui2/
DCM4CHEE_WEB_USERNAME=dcm4chee
DCM4CHEE_UI_PASSWORD=<DCM4CHEE_UI_PASSWORD>
DICOMWEB_BASE=/dcm4chee-arc/aets/DCM4CHEE/rs
DICOM_AE_TITLE=DCM4CHEE
DICOM_PORT=11112 (127.0.0.1 only until you run: sudo dcm4chee-dimse allow <AET> && sudo dcm4chee-dimse open)
The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses. To use your own DNS name, see Step 9.
The built in self test proves the whole chain from the command line. On an empty archive it stores the synthetic test study over DICOMweb, queries and retrieves it, and deletes that study again by its UID; when the archive already holds other studies it writes nothing and runs only the read only checks:
sudo dcm4chee-selftest
SELFTEST-OK + STOW/QIDO/WADO round trip (phantom stored, then rejected and deleted by its StudyInstanceUID) https 301 + /healthz + 401 without/with a wrong/default password on every path + archive STARTED + 403 cross-site/none/foreign-Origin on a guarded route + 403 cross-origin POST + 444 unknown host + LDAP/PostgreSQL upstream defaults refused + no host-side WildFly management + DIMSE on 127.0.0.1 + IMDS blocked in arc/ldap/db
Step 5: Sign in to the web interface
Open the DCM4CHEE_URL in your browser. The certificate is self signed, so your browser asks you to confirm it the first time. Before you accept it, compare the fingerprint your browser shows with the one on the instance (or install your own certificate, Step 9):
sudo openssl x509 -in /etc/ssl/dcm4chee/dcm4chee.crt -noout -fingerprint -sha256
Your browser then asks for a user name and password: enter dcm4chee and the DCM4CHEE_UI_PASSWORD from Step 4. The UI2 interface opens on the Studies page, where you search studies by patient, date, modality and more.

Step 6: Store and find a study over DICOMweb
The same user name and password protect the REST API and DICOMweb. From the instance itself, with curl, store the synthetic test study with STOW-RS and query it back with QIDO-RS (the password goes into a private curl config file, never onto the command line, and the file is removed at the end):
F=/root/dcm4chee-arc-light-credentials.txt
H=$(sudo sed -n 's#^DCM4CHEE_URL=https://\([^/]*\)/.*#\1#p' $F)
( umask 077; printf 'user = "dcm4chee:%s"\n' "$(sudo sed -n 's/^DCM4CHEE_UI_PASSWORD=//p' $F)" > ~/.dcm4chee-curl )
B=cloudimgGuide; rm -f /tmp/stow.body
for f in /usr/share/dcm4chee-arc-light/sample-dicom/*.dcm; do printf -- '--%s\r\nContent-Type: application/dicom\r\n\r\n' $B >> /tmp/stow.body; cat "$f" >> /tmp/stow.body; printf '\r\n' >> /tmp/stow.body; done
printf -- '--%s--\r\n' $B >> /tmp/stow.body
curl -s -K ~/.dcm4chee-curl --resolve "$H:443:127.0.0.1" --cacert /etc/ssl/dcm4chee/dcm4chee.crt -o /dev/null -w 'STOW-RS -> HTTP %{http_code}\n' \
-X POST -H "Content-Type: multipart/related; type=\"application/dicom\"; boundary=$B" --data-binary @/tmp/stow.body \
"https://$H/dcm4chee-arc/aets/DCM4CHEE/rs/studies"
curl -s -K ~/.dcm4chee-curl --resolve "$H:443:127.0.0.1" --cacert /etc/ssl/dcm4chee/dcm4chee.crt \
"https://$H/dcm4chee-arc/aets/DCM4CHEE/rs/studies?PatientID=CLOUDIMG-PHANTOM-001" | grep -o '"PHANTOM^CLOUDIMG"' | head -1
rm -f /tmp/stow.body ~/.dcm4chee-curl
STOW-RS -> HTTP 200
"PHANTOM^CLOUDIMG"
In the web interface, on the Studies page choose DCM4CHEE as the Web App Service, enter CLOUDIMG-PHANTOM-001 as the Patient Identifier and choose SUBMIT. Expand the study with its arrow to see its two series:


DICOMweb clients and viewers point at https://<your-host>/dcm4chee-arc/aets/DCM4CHEE/rs with HTTP Basic authentication. The routes that make the archive contact other systems (/dcm4chee-arc/aets/<AET>/dimse/..., /diff/..., /pdq/..., /xroad/..., /impax/..., /token2/..., /host/...) are refused with 403 unless the request carries Sec-Fetch-Site: same-origin (browsers send it on the UI's own requests); a script that calls them sends that header itself. A request that changes data is refused with 403 when it carries another web site's Origin.
When you are done with the test study, delete it in the UI (the study's menu offers reject and then delete), or with curl as above: POST .../rs/studies/<study-uid>/reject/113039%5EDCM, then DELETE https://$H/dcm4chee-arc/aets/IOCM_REGULAR_USE/rs/studies/<study-uid>.
Step 7: Change the password
To replace the web password with a new random one (written to the credentials file; the old one stops working at once):
sudo dcm4chee-set-password
Your browser may keep offering the old password until you close it.
Step 8: Accept DICOM from your modalities (DIMSE)
DICOM DIMSE (C-STORE, C-FIND, C-MOVE, C-GET to AE title DCM4CHEE on port 11112) has no password. The archive also answers on the same port as several other AE titles (WORKLIST, AS_RECEIVED and the IOCM_* titles used for rejection workflows). Before you publish the port, tell the archive which calling AE titles it may accept; dcm4chee-dimse allow sets that allowlist on every network AE of the archive. Then open the port and allow it in the security group only from your modalities' addresses:
sudo dcm4chee-dimse status
DIMSE bind address: 127.0.0.1 (port 11112, AE title DCM4CHEE)
listening: 127.0.0.1:11112
AE AS_RECEIVED (installed TRUE): accepted calling AE titles: ANY (no allowlist)
AE DCM4CHEE (installed TRUE): accepted calling AE titles: ANY (no allowlist)
AE IOCM_EXPIRED (installed TRUE): accepted calling AE titles: ANY (no allowlist)
...
AE WORKLIST (installed TRUE): accepted calling AE titles: ANY (no allowlist)
allowlist: NOT set on every installed AE (open is refused)
sudo dcm4chee-dimse allow <MODALITY_AET> <ANOTHER_AET>
sudo dcm4chee-dimse open
open refuses unless every installed AE carries the allowlist, and restarts the archive container with port 11112 published on all interfaces (about a minute). A port published by Docker is not filtered by ufw: the security group is what limits who can connect, so add an inbound rule for TCP 11112 from your modalities only. An AE title is not authentication (anyone who can reach the port can claim one), and the allowlist does not limit which studies an accepted AE sees; keep 11112 private to your imaging network. In the UI you can additionally register each modality as a device and switch on Validate Calling AE Hostname for the DCM4CHEE AE.
Every start of the archive checks this again: if DIMSE is open but any installed AE has lost its allowlist (for example after you add an AE in the UI), it is closed back to localhost. A quick DICOM C-ECHO from the instance shows the allowlist at work (ACCEPTED for a listed AE title, REJECTED for any other; add a called AE title as a third argument to test another AE):
sudo dcm4chee-dimse echo <MODALITY_AET>
To take DIMSE back to localhost: sudo dcm4chee-dimse close. To test with your own tools, DCMTK's echoscu/storescu (storescu -aet <MODALITY_AET> -aec DCM4CHEE <host> 11112 <files>) or upstream's dcm4che/dcm4che-tools image both work.
Step 9: Use your own host name and certificate
Point a DNS record at the instance (an Elastic IP keeps the address across stop and start), then tell the archive its name. This mints a new self signed certificate for that name, updates the nginx host rule and the cross site guards, and keeps the name across reboots:
sudo dcm4chee-set-hostname pacs.example.com
To use a certificate from a public authority instead, replace /etc/ssl/dcm4chee/dcm4chee.crt (full chain) and /etc/ssl/dcm4chee/dcm4chee.key (mode 0600, owned by root) with your own and run sudo systemctl reload nginx. Running dcm4chee-set-hostname again later would replace them with a new self signed pair.
Without your own name, each boot checks the instance's names: after a stop and start that changed the public address, the next boot issues a new certificate for the new name. Nothing changes on a boot where the names are the same.
Step 10: Storage, backups and persistence
All archive state is on the data volume:
findmnt -no SOURCE,SIZE,TARGET /var/lib/dcm4chee
sudo ls /var/lib/dcm4chee
/dev/nvme1n1 31.2G /var/lib/dcm4chee
db
ldap
lost+found
slapd.d
storage
wildfly
To grow it, increase the volume's size in the EC2 console (or aws ec2 modify-volume), then sudo resize2fs /dev/nvme1n1 (check the device name with findmnt). The simplest complete backup is an EBS snapshot of both volumes (or AWS Backup on the instance); for a consistent snapshot, stop the stack first with sudo systemctl stop dcm4chee-arc and start it again afterwards.
Every service is enabled, so the archive returns after a reboot or a stop and start, and unattended security upgrades keep the operating system, nginx and Docker patched:
systemctl is-enabled docker dcm4chee-arc dcm4chee-set-host nginx unattended-upgrades
enabled
enabled
enabled
enabled
enabled
The secrets the containers use are files under /etc/dcm4chee (root only). The compose project is /opt/dcm4chee/compose.yaml; its images are pinned and are never pulled at boot.
Security notes
- No shared secret ships in the image. No password, password hash, TLS key or database exists until your instance's first boot creates them, and nginx does not start before that has finished. Every secret reaches the containers as a read only file, never as a container environment value.
- The web wall. Upstream's "light" archive has no authentication; nginx requires the per instance password on every path except
/healthz, and only/dcm4chee-arc/is passed to the archive. From the instance itself only root and nginx can reach the archive's port 8080, and only root can reach the containers' own addresses. - Unfixed upstream advisories, closed by configuration. GHSA-q39p-rchv-x498 (DIMSE accepts any calling AE title): DIMSE is on localhost until you set an allowlist (Step 8). GHSA-q47j-x5mg-ff23 (unauthenticated HL7 patient merge): HL7 is never published. The other advisories published for dcm4chee-arc-light are fixed in 5.35.2.
- Reviewed residuals in the upstream images. The archive image's Amazon Linux packages and some WildFly libraries carry published advisories that upstream's next release (on WildFly 41.0.1 and Corretto 21.0.13) will clear. None is reachable as shipped: the archive's Java process loads none of the flagged system libraries, no TLS listener of the archive is published, the library advisories are denial of service issues reachable only by the signed in administrator, and the compromised emSign and eMudhra roots are removed from the archive's Java truststore. In the PostgreSQL image, the flagged brotli and Go libraries belong to tools the database does not run. cloudimg rebuilds this version when upstream ships the update.
- No access to the metadata service. The containers, Docker and nginx are blocked from the EC2 instance metadata address.
- Restrict 22/tcp to your management network, 80/443 to the networks that use the archive, and 11112 (if you open it) to your modalities.
Architecture summary
| Component | Detail |
|---|---|
| Archive | dcm4chee-arc-light 5.35.2 (MPL 1.1 / GPL 2.0 / LGPL 2.1) on WildFly 41, container arc, HTTP on 127.0.0.1:8080, DIMSE on 127.0.0.1:11112 (AE DCM4CHEE) |
| Configuration store | OpenLDAP 2.6, container ldap, not published |
| Database | PostgreSQL 18.6, container db, not published |
| Web server | nginx on 443 (HTTPS, per instance certificate in /etc/ssl/dcm4chee), 80 redirects to HTTPS, /healthz for load balancer checks |
| Data | /var/lib/dcm4chee on its own EBS volume (LDAP, PostgreSQL, WildFly state, DICOM objects) |
| Credentials | /root/dcm4chee-arc-light-credentials.txt, mode 0600, root only; container secrets in /etc/dcm4chee |
| Helpers | dcm4chee-selftest, dcm4chee-set-password, dcm4chee-set-hostname, dcm4chee-dimse (in /usr/local/sbin) |
| First boot | dcm4chee-arc-firstboot.service; the stack dcm4chee-arc.service; host name check on every later boot dcm4chee-set-host.service |
| Ports | 22 SSH, 80 redirect, 443 archive; 11112 DIMSE only after dcm4chee-dimse open |
| Base image | Ubuntu 24.04 LTS, fully patched, unattended security upgrades enabled, IMDSv2 required |
Support
cloudimg provides 24/7 support for this image, the Ubuntu operating system and its deployment. Email support@cloudimg.co.uk with your AWS account ID and the instance ID. For dcm4chee-arc-light itself, see the dcm4chee-arc-light project on GitHub and its wiki. dcm4chee and dcm4che are open source projects; cloudimg is not affiliated with or endorsed by them. DICOM is the registered trademark of NEMA for its standards publications relating to digital communications of medical information.