DFIR-IRIS on Ubuntu 24.04 on Azure User Guide
Overview
DFIR-IRIS is an open source collaborative platform for digital forensics and incident response teams. It gives analysts a shared case workspace with assets, indicators of compromise, a structured investigation timeline, evidence and datastore management, tasks and notes, so an incident is worked in one place with a full audit trail. A complete REST API and a pluggable module system let you drive it from your existing tooling.
The cloudimg image runs DFIR-IRIS v2.4.29 in a dedicated Python virtual environment served by gunicorn with the eventlet worker, bound to loopback and reverse proxied behind nginx which terminates TLS. PostgreSQL holds durable state and RabbitMQ drives a Celery worker for module pipelines and report generation. Every secret, including the administrator password, the REST API key, the database and broker passwords, the Flask secret key and password salt, and the TLS certificate, is generated uniquely on the first boot of each instance, so nothing ships with a known password.
What is included:
- DFIR-IRIS v2.4.29 from the official source tag at
/opt/iris-web, served by gunicorn (eventlet) - nginx TLS reverse proxy on port 443, a port 80 redirect, and an unauthenticated static
/healthzon both for load balancer probes - PostgreSQL 16 holding the
iris_dbandiris_tasksdatabases with per VM application roles - RabbitMQ bound to loopback as the Celery broker, with a per VM broker user and the default
guestaccount removed - A Celery worker (
iris-worker.service) for modules, pipelines and report generation - A single
administratoraccount created automatically at first boot with a per VM password and REST API key - A per VM self signed TLS certificate carrying the instance address in its SAN
iris-web-firstboot.servicegenerating every secret, building the database and provisioning the administrator- DFIR-IRIS, the worker, nginx, PostgreSQL and RabbitMQ enabled and auto starting on boot
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key, and a VNet with a subnet. Standard_B2s (2 vCPU, 4 GB RAM) is a comfortable starting point for a small team; move up to a D2s or D4s size for larger teams or heavy module and reporting workloads. Open inbound ports 443 and 80 (and 22 to your management network) on the network security group. PostgreSQL, RabbitMQ and the gunicorn application all listen only on loopback and are never exposed to the network.
Step 1: Deploy and connect
Deploy the image from the Azure Marketplace, then connect over SSH using the admin username you chose at deployment time:
ssh azureuser@<vm-ip>
First boot generates every secret and builds the database. It normally completes within a minute or two of the VM reaching the running state. Confirm it has finished before going further:
sudo systemctl is-active iris-web-firstboot.service
ls -l /var/lib/cloudimg/iris-web-firstboot.done
is-active reports active and the sentinel file exists once first boot is complete.
Step 2: Verify the services
Five services make up the appliance. All five should report active:
sudo systemctl is-active iris-web iris-worker nginx postgresql rabbitmq-server

Step 3: Confirm the network exposure
Only nginx faces the network. The application, PostgreSQL, RabbitMQ, the Erlang distribution port and the epmd port mapper are all bound to loopback:
sudo ss -tlnp | grep -E ':80 |:443 |:8000 |:5432 |:5672 '
You should see nginx on 0.0.0.0:80 and 0.0.0.0:443, and gunicorn, PostgreSQL and RabbitMQ on 127.0.0.1 only.
The unauthenticated health endpoint is served over plain HTTP for load balancer probes, while everything else redirects to TLS:
curl -s http://127.0.0.1/healthz
curl -s -o /dev/null -w 'http root -> %{http_code}\n' http://127.0.0.1/
The first returns ok and the second reports 301.
Step 4: Read the per VM credentials
Every secret is unique to this instance and is written to a root only file:
sudo cat /root/iris-web-credentials.txt

The IRIS_URL line records the address the instance resolved for itself at first boot. On a VM with a public IP, browse to that public IP or to whatever DNS name you point at the VM; DFIR-IRIS derives its URLs from the request host, so it renders correctly on any address you reach it on.
Check the version, the health endpoint and the TLS certificate that was generated for this VM:
curl -s http://127.0.0.1/healthz
curl -sk https://127.0.0.1/api/versions
sudo openssl x509 -in /etc/iris/tls/iris.crt -noout -subject -dates

Step 5: Sign in
Open https://<vm-ip>/ in a browser. The certificate is self signed and generated for this VM, so your browser will warn on the first visit; accept it, or replace the certificate as shown in Step 9.
Sign in as administrator with the IRIS_ADMIN_PASSWORD value from the credentials file.

After signing in you land on the dashboard, which summarises open cases, tasks and alerts.

A single case, #1 - Initial Demo, is created by DFIR-IRIS itself so the interface is never empty. The cases list is under Manage cases.

Step 6: Create your first case
From the dashboard choose Create new case, or open Manage cases and select Add case. Fill in a case name, a description, the customer and your SOC reference, then save. The screenshots in this guide were produced with:
- Case name —
Ransomware outbreak - finance VLAN - Description —
Suspected ransomware detected on the finance VLAN following an EDR alert. Scoping impacted hosts and collecting volatile evidence. - Customer —
IrisInitialClient - SOC ID —
SOC-2026-0042
Once created, the case summary is the hub for the whole investigation, with tabs for notes, assets, indicators, the timeline, the graph, tasks and evidence.

Step 7: Record indicators, assets and evidence
Inside the case, use the IOC tab and Add IOC to record indicators. The examples shown here are an ip-any indicator 198.51.100.51 described as a C2 beacon, and an md5 indicator for the dropped encryptor, both at TLP amber.

Use the Assets tab and Add asset to record affected hosts, for example a Windows - Computer asset named FIN-WKSTN-014 described as patient zero.

Use the Evidence tab and Register Evidence to record collected artefacts with their size and hash, which is what preserves chain of custody.

Step 8: Use the REST API
DFIR-IRIS exposes a full REST API authenticated with the per VM key from the credentials file. List the cases on the instance:
IRIS_KEY="<IRIS_ADMIN_API_KEY>"
curl -sk -H "Authorization: Bearer ${IRIS_KEY}" https://127.0.0.1/manage/cases/list | python3 -m json.tool | head -20

Create a case, then add an indicator to it:
IRIS_KEY="<IRIS_ADMIN_API_KEY>"
CASE_ID=$(curl -sk -H "Authorization: Bearer ${IRIS_KEY}" -H 'Content-Type: application/json' \
-X POST https://127.0.0.1/manage/cases/add \
-d '{"case_name":"Phishing triage","case_description":"Reported credential phishing campaign.","case_customer":1,"case_soc_id":"SOC-2026-0043"}' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["data"]["case_id"])')
echo "created case ${CASE_ID}"
curl -sk -H "Authorization: Bearer ${IRIS_KEY}" -H 'Content-Type: application/json' \
-X POST "https://127.0.0.1/case/ioc/add?cid=${CASE_ID}" \
-d '{"ioc_value":"203.0.113.24","ioc_type_id":76,"ioc_tlp_id":2,"ioc_description":"Phishing landing page host","ioc_tags":"phishing"}' \
| python3 -m json.tool | head -12
Authentication is enforced server side. The identical request without a key is refused:
curl -sk -o /dev/null -w 'unauthenticated case creation -> %{http_code}\n' \
-H 'Content-Type: application/json' -X POST https://127.0.0.1/manage/cases/add \
-d '{"case_name":"should be refused","case_description":"no credential","case_customer":1,"case_soc_id":"NOPE"}'
This reports 401.
Step 9: Replace the TLS certificate
The appliance generates a self signed certificate for the instance at first boot. For production use, replace it with a certificate issued by your own CA or a public CA and reload nginx:
sudo ls -l /etc/iris/tls/
Copy your certificate to /etc/iris/tls/iris.crt and your private key to /etc/iris/tls/iris.key, keep the key as 0600 root:root, then run sudo nginx -t && sudo systemctl reload nginx.
Step 10: Components
| Component | Location |
|---|---|
| Application source | /opt/iris-web/source |
| Python 3.9 virtual environment | /opt/iris-web/venv |
| Environment and secrets | /etc/iris/iris.env (0640 root:iris) |
| TLS certificate and key | /etc/iris/tls/ |
| Uploads, templates, backups | /home/iris/ |
| Per VM credentials | /root/iris-web-credentials.txt (0600 root:root) |
| nginx site | /etc/nginx/sites-available/cloudimg-iris-web |
| Web service | iris-web.service |
| Celery worker | iris-worker.service |
| First boot service | iris-web-firstboot.service |
Useful commands:
sudo systemctl status iris-web --no-pager | head -12
sudo journalctl -u iris-worker --no-pager -n 10
Step 11: Security notes
Every secret on this instance is generated at first boot and is unique to it. There is no default administrator password and no shared key.
The image also corrects four issues present in the upstream project, all of which matter for a platform that holds forensic evidence:
- Development TLS private keys are not shipped. The upstream repository commits a working root CA private key, whose certificate remains valid until 2032, along with a development server key, and its sample environment points nginx at them. Anyone holding that CA key could mint certificates trusted by any deployment that installed it. Neither key is present in this image; a fresh keypair is generated per VM instead.
- No upstream default secrets are in use. The upstream sample environment ships a syntactically valid Flask secret key and password salt, so a careless deployment runs with a publicly known session key. This image generates both per VM and fails the build if either default string is present.
- Nothing but nginx faces the network. Upstream binds the application, PostgreSQL and RabbitMQ to all interfaces and relies on the container network for isolation, which does not exist in a native install. Here they are all bound to loopback.
- The case Assets tab renders correctly for assets created through the REST API. In upstream v2.4.29 the assets table crashes while rendering if an asset has no analysis status, which is exactly what the REST API produces when
analysis_status_idis omitted, leaving that case's Assets tab loading forever. This image includes a null guard so API created assets display normally with anUnspecifiedstatus.
The administrator password is also recorded in the credentials file only; the image deliberately provisions the administrator before the service starts so that the password is never written to the system journal.
Recommended next steps: replace the TLS certificate (Step 9), create individual analyst accounts under Manage rather than sharing the administrator login, review the access control groups, and restrict inbound access to your analyst networks with a network security group rule.
Licensing
DFIR-IRIS is distributed under the GNU Lesser General Public License v3.0 or later. This is a repackaged open source software product with additional charges for cloudimg support services. DFIR-IRIS is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.