dcm4chee-arc-light 5.35.2
Overview
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) and indexes them in PostgreSQL. It 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.
This guide covers launching and using the SecureAnyCloud dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 AMI on Amazon EC2. It is one version of the SecureAnyCloud file sharing and sync listing on AWS Marketplace, and you select it as the software version when you subscribe and launch. Upstream ships the archive only as container images, so this AMI runs the upstream stack (an OpenLDAP configuration store, PostgreSQL and the WildFly-based archive) under Docker Compose and adds the security work described below.
What you get:
- 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 that 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, whereas upstream ships one key that is shared by every copy of its image. - DICOM DIMSE stays on localhost until you decide. DICOM associations carry no password, and 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. From the instance itself, they are reachable 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. 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. - A fully patched base. Ubuntu 24.04 LTS is patched 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 that 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. The image is not certified for any regulatory scheme.
Prerequisites
Before you deploy, make sure you have:
- 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 that 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 port 11112 until Step 8.
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, select your Region, and choose Launch through EC2.
Choose m5.large (or larger), your key pair, a security group as described above, and a subnet. Under Configure storage, you 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.
To launch with the AWS CLI instead, 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}]'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>Check the services
First boot generates every password, key and certificate, creates the LDAP and PostgreSQL databases, and starts the archive. This takes about a minute:
systemctl is-active docker sac-dcm4chee-netguard dcm4chee-arc-firstboot dcm4chee-arc nginxactive active active active activeThe three containers run under Docker Compose:
sudo docker compose -f /opt/dcm4chee/compose.yaml ps --format '{{.Service}} {{.State}}'arc running db running ldap runningOnly 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 -u0.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 [::]:80If nginx is not active, first boot has not finished, or it failed, in which case nginx stays down. Check systemctl status dcm4chee-arc-firstboot and journalctl -u dcm4chee-arc-firstboot.
Get your URL and password
sudo cat /root/dcm4chee-arc-light-credentials.txtThe file is readable by root only:
# dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 (sac). 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 checks 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, the self-test writes nothing and runs only the read-only checks:
sudo dcm4chee-selftestSELFTEST-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/dbSign 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 that 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 -sha256Your 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 can search studies by patient, date, modality and more.
Store and find a study over DICOMweb
The same user name and password protect the REST API and DICOMweb. From the instance itself, use curl to store the synthetic test study with STOW-RS and to 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=sacGuide; 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=SAC-PHANTOM-001" | grep -o '"PHANTOM^SAC"' | head -1 rm -f /tmp/stow.body ~/.dcm4chee-curlSTOW-RS -> HTTP 200 "PHANTOM^SAC"In the web interface, open the Studies page and choose DCM4CHEE as the Web App Service. Enter SAC-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 this header on the UI's own requests, and a script that calls these routes must send the 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 use 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>.
Change the password
To replace the web password with a new random one, run the following command. The new password is written to the credentials file, and the old one stops working at once:
sudo dcm4chee-set-passwordYour browser may keep offering the old password until you close it.
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 statusDIMSE 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 openopen refuses unless every installed AE carries the allowlist. It restarts the archive container with port 11112 published on all interfaces, which takes about a minute. Docker-published ports are not filtered by ufw, so the security group is what limits who can connect. Add an inbound rule for TCP 11112 from your modalities only.
An AE title is not authentication, because anyone who can reach the port can claim one. The allowlist also does not limit which studies an accepted AE sees, so keep port 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), DIMSE is closed back to localhost. A quick DICOM C-ECHO from the instance shows the allowlist at work. It returns ACCEPTED for a listed AE title and 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, run sudo dcm4chee-dimse close. To test with your own tools, use DCMTK's echoscu/storescu (storescu -aet <MODALITY_AET> -aec DCM4CHEE <host> 11112 <files>) or upstream's dcm4che/dcm4che-tools image. Both work.
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 creates 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.comTo 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, then run sudo systemctl reload nginx. Running dcm4chee-set-hostname again later replaces 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.
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 wildflyTo grow the volume, increase its size in the EC2 console (or aws ec2 modify-volume), then run 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. Unattended security upgrades keep the operating system, nginx and Docker patched:
systemctl is-enabled docker dcm4chee-arc dcm4chee-set-host nginx unattended-upgradesenabled enabled enabled enabled enabledThe secrets that 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. For GHSA-q39p-rchv-x498 (DIMSE accepts any calling AE title), DIMSE is on localhost until you set an allowlist (Step 8). For 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, and 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 that the database does not run. SecureAnyCloud 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 network access. Limit 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
SecureAnyCloud provides 24/7 support for this image, the Ubuntu operating system and its deployment. Email sales@secureanycloud.com 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, and SecureAnyCloud 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.
Deploy on AWS
Launch dcm4chee-arc-light 5.35.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com