Blaze FHIR Server 1.11.0
Overview
This guide covers launching and using the Blaze FHIR Server 1.11.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened EHRbase & GIS, Health and Research Platforms AMIs listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
Blaze is an open-source FHIR R4 server (Apache-2.0, github.com/samply/blaze). It stores clinical and research resources such as Patient, Observation, Encounter and Condition. It answers the FHIR REST API (create, read, update, delete, history, search with chaining and includes, transactions and batches) and evaluates CQL measures. Everything runs in one process with an embedded RocksDB store, so there is no separate database to run.
Blaze is a JSON REST API without a browser interface. You use it with curl, a FHIR client library or a tool such as Postman.
What you get:
- HTTPS from the first boot. nginx serves the FHIR API on port 443 with a certificate generated on your instance's first boot (never baked into the image). Port 80 redirects to HTTPS.
- A password before anything answers. Blaze ships with no authentication. On this image nginx requires HTTP Basic authentication for every FHIR request, with a random password created for user
adminon your instance's first boot and stored only as a bcrypt hash. Nothing but SSH answers until first boot has created it, and the credential is never passed on to Blaze. - Blaze 1.11.0, pinned. The upstream
samply/blaze1.11.0 container is pinned by its image digest and never pulled at boot. Version 1.11.0 carries the Jetty 12.1.12 and netty 4.2.17 security fixes that 1.10.1 lacks. - Its own data volume. The RocksDB store lives on a dedicated 20 GiB gp3 EBS volume mounted at
/var/lib/blaze, which you can grow or snapshot on its own. - Blaze on loopback, unprivileged. The container is published on
127.0.0.1:8080only, runs as the unprivilegedblazeaccount with every Linux capability dropped, and is reached only through nginx. - Fixed host and real client addresses. Absolute URLs in Blaze's responses (
Bundle.link,fullUrl,Location) always use your configured host name, even if a client forges forwarding headers. Requests for a host name the instance does not serve are dropped, cross-origin write requests from other websites are refused, and nginx logs the real client address. - Metadata service blocked. The Blaze container, Docker and nginx cannot reach the instance metadata service, so a URL configured into Blaze can never reach it, and the AMI requires IMDSv2.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on for Ubuntu and for Docker's repository.
What is on the image: Blaze 1.11.0 (container samply/blaze, Eclipse Temurin 25), Docker Engine, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu | Architecture: x86_64 | Recommended instance type: m5.large
Prerequisites
You need:
- 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) from your management network and TCP 443 (the FHIR API) and TCP 80 (redirects to HTTPS) from the networks your FHIR clients connect from. Never open port 8080.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Blaze FHIR Server 1.11.0 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2. Choose m5.large, your key pair and a security group as above.
First boot gives Blaze a quarter of the instance memory as JVM heap and another quarter as RocksDB block cache. A larger instance therefore serves larger data sets without any change. Step 9 shows how to adjust both.
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=blaze}]'Connect to your instance
Version SSH user
Blaze FHIR Server 1.11.0 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot takes about a minute. It creates the certificate and the password, sizes the memory settings, and starts Blaze against its empty store. Only then does nginx start. After that, every unit is active and the Blaze container is up, published on loopback only:
systemctl is-active docker blaze nginx blaze-firstboot
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'active active active active NAMES STATUS PORTS blaze Up 2 minutes 127.0.0.1:8080->8080/tcpThe image runs exactly one container image, pinned by digest:
sudo docker image ls --digests --format '{{.Repository}}@{{.Digest}}'samply/blaze@sha256:1a60d9ca0d579d8296b65ae3c24fea838730e4267e39aa7676ebf945140be3cbCheck what the network can reach
sudo /usr/local/sbin/blaze-port-check.shoff-box TCP exactly [22 80 443]; Blaze on loopback 8080 BLAZE_PORTS_OKThis is the complete network surface of a new instance: SSH, the HTTPS front door and the HTTP redirect. Blaze itself listens on loopback only.
Read your password
First boot writes this instance's FHIR base URL, the user name and its password to a root-only file:
sudo cat /root/blaze-credentials.txt# Blaze FHIR Server 1.11.0 on Ubuntu 24.04 (sac)
# Generated on this instance's first boot. These credentials are unique to THIS instance. BLAZE_URL=https://<public-dns-name>/ BLAZE_FHIR_BASE=https://<public-dns-name>/fhir BLAZE_USERNAME=admin BLAZE_PASSWORD=<BLAZE_PASSWORD> ...None of these values exists in the image. The password and the certificate are created on your instance's first boot. The host name is your instance's public DNS name (or its IP address when it has none). Step 8 shows how to use your own.
Make your first FHIR request
Without the password every FHIR request is refused by nginx before it reaches Blaze, and port 80 only redirects to HTTPS on your configured host name:
curl -sk -o /dev/null -w 'no password: %{http_code}\n' https://127.0.0.1/fhir/metadata
curl -s -o /dev/null -w 'port 80: %{http_code} -> %{redirect_url}\n' http://127.0.0.1/fhir/metadatano password: 401 port 80: 301 -> https://<public-dns-name>/fhir/metadataThe commands on this page read the password from your credentials file and hand it to curl as a configuration file on its standard input (-K -). The password therefore never appears on a command line or in your shell history. Define this small helper in your SSH session:
blaze_auth() {
sudo sed -n 's/^BLAZE_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/blaze-credentials.txt; } blaze_auth | curl -sk -K - -H 'Accept: application/fhir+json' https://127.0.0.1/fhir/metadata \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["resourceType"], d["software"]["name"], d["software"]["version"], "FHIR", d["fhirVersion"]); print(d["implementation"]["url"])'CapabilityStatement Blaze 1.11.0 FHIR 4.0.1 https://<public-dns-name>/fhirThe CapabilityStatement lists every resource type, interaction and search parameter the server supports. From your own workstation the same request is curl -k -u admin https://<public-dns-name>/fhir/metadata, which prompts for the password.
Create, read and search resources
Create a Patient. Blaze assigns the id and the version:
blaze_auth() {
sudo sed -n 's/^BLAZE_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/blaze-credentials.txt; } blaze_auth | curl -sk -K - -X POST -H 'Content-Type: application/fhir+json' -H 'Prefer: return=representation' \
--data '{"resourceType":"Patient","identifier":[{"system":"urn:example:mrn","value":"MRN-0001"}],"name":[{"family":"Lovelace","given":["Ada"]}],"gender":"female","birthDate":"1815-12-10"}' \
https://127.0.0.1/fhir/Patient \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["resourceType"], d["id"], "version", d["meta"]["versionId"], d["name"][0]["family"])'Patient DIKZ3OG7UZOMNONF version 7 LovelaceSearch for it by its identifier. The fullUrl of every entry is built from your configured host name:
blaze_auth() {
sudo sed -n 's/^BLAZE_PASSWORD=\(.*\)/user = "admin:\1"/p' /root/blaze-credentials.txt; } blaze_auth | curl -sk -K - -H 'Accept: application/fhir+json' 'https://127.0.0.1/fhir/Patient?identifier=urn:example:mrn|MRN-0001' \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print("total:", d["total"]); [print(e["fullUrl"]) for e in d.get("entry", [])]'total: 1 https://<public-dns-name>/fhir/Patient/DIKZ3OG7UZOMNONFTo load data in bulk, POST a FHIR transaction or batch Bundle to https://<public-dns-name>/fhir.
The Blaze project's separate command-line tool blazectl (github.com/samply/blazectl, not installed on the image) uploads directories of bundles, counts resources and downloads search results from your workstation. Point it at your FHIR base URL with your admin password.
Use your own DNS name and certificate
Create a DNS record for your instance (for example fhir.example.com), then serve Blaze under that name. The helper creates a new certificate for it and pins nginx and the cross-origin guard to it. It also sets Blaze's base URL and restarts Blaze:
sudo blaze-set-hostname fhir.example.comThe name is remembered in /etc/sac/blaze-hostname. To use a certificate from your own certificate authority, replace /etc/ssl/blaze/blaze.crt and /etc/ssl/blaze/blaze.key (keep the key 0600 root). Then run sudo systemctl reload nginx.
Storage, memory and the password
The RocksDB store is on its own EBS volume:
findmnt -n -o SOURCE,SIZE,FSTYPE /var/lib/blaze
sudo ls -l /var/lib/blaze cat /etc/blaze-sac/blaze-tuning.env/dev/nvme1n1 19.5G ext4 total 20 drwxr-xr-x 2 blaze blaze 4096 Oct 20 15:11 admin-index drwxr-xr-x 2 blaze blaze 4096 Oct 20 15:11 admin-transaction drwxr-xr-x 2 blaze blaze 4096 Oct 20 15:11 index drwxr-xr-x 2 blaze blaze 4096 Oct 20 15:11 resource drwxr-xr-x 2 blaze blaze 4096 Oct 20 15:11 transaction
# Rendered by first boot from this instance memory (7816 MiB) (sac). Edit and `sudo systemctl restart blaze`. JAVA_TOOL_OPTIONS=-Xmx1954m DB_BLOCK_CACHE_SIZE=1954- Grow the store. Enlarge the volume in the EC2 console, then run
sudo resize2fson the device shown byfindmnt. Snapshot the volume to back up your data. Stop Blaze (sudo systemctl stop blaze) first for a consistent snapshot. - Memory. Edit
/etc/blaze-sac/blaze-tuning.env(JVM heap and RocksDB block cache in MiB) and runsudo systemctl restart blaze. You can add other Blaze settings from its documentation to the same file. - Password.
sudo blaze-set-passwordcreates a new random password and updates/root/blaze-credentials.txt.sudo blaze-set-password --stdinreads a password you choose (12 or more characters) from standard input. nginx picks it up on the next request.
Metadata access is blocked
Blaze can be configured to call other services, such as an OpenID Connect provider or an external terminology server. The image drops any container traffic to the instance metadata service in Docker's DOCKER-USER firewall chain. Docker, Blaze's unit and nginx are also denied it by systemd:
sudo iptables -S DOCKER-USER
sudo docker exec blaze curl -s -m 5 -o /dev/null -w 'metadata from inside the container: %{http_code}\n' http://169.254.169.254/latest/meta-data/ || true-N DOCKER-USER -A DOCKER-USER -d 168.63.129.16/32 -j DROP -A DOCKER-USER -d 169.254.169.254/32 -j DROP metadata from inside the container: 000000 means the connection never completed. The instance itself still uses IMDSv2 normally (first boot reads its host name from it).
Updates
- Ubuntu and Docker receive security updates automatically through
unattended-upgrades. - Blaze is the pinned 1.11.0 container. SecureAnyCloud publishes a new version of this listing when a new Blaze release is needed. To move an existing instance to it, snapshot the data volume, launch the new version, and attach the volume (or restore its snapshot) at /var/lib/blaze.
Security recommendations
- Open port 443 only to the networks your FHIR clients use, and port 22 only to your management network.
- Use your own DNS name and a certificate your clients trust (Step 8) before connecting production systems.
- The HTTP Basic password is a single shared credential for the whole server. For per-client access control, enable Blaze's OpenID Connect authentication (see the Blaze documentation) behind the same nginx front door.
- This image is infrastructure. If you store protected health information, you are responsible for the controls it needs: encryption, access control, audit, backups and the regulations that apply to you, such as HIPAA or GDPR. The image is not a certified compliant product.
Support and licensing
SecureAnyCloud provides 24/7 support for this image at sales@secureanycloud.com.
Blaze is licensed under the Apache License 2.0. The license and this build's provenance are in /usr/share/doc/blaze-sac/.
Blaze is developed by the samply project. This image is provided by SecureAnyCloud and is not affiliated with or endorsed by the Blaze maintainers.
Deploy on AWS
Launch Blaze FHIR Server 1.11.0 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