Community Health Toolkit 5.3.3 on AWS User Guide
Overview
This guide covers launching and using the Community Health Toolkit 5.3.3 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened EHRbase & GIS, Health and Research Platforms AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
The Community Health Toolkit (CHT) is the open source platform, stewarded by Medic, for building digital health apps used by community health workers and the health facilities that support them: registering households and people, guided visits and assessments, tasks and follow-ups, targets, SMS workflows and offline-first mobile use. CHT Core is the server those apps run on. This image gives you a working CHT Core server with Medic's default configuration; you then deploy your own project configuration to it with cht-conf.
What is on the image:
- CHT Core 5.3.3 from Medic's six official container images, unmodified and each pinned by digest:
cht-nginx,cht-api,cht-sentinel,cht-haproxy,cht-couchdb(CouchDB, single node) andcht-couchdb-nouveau(search), started with Medic's own Docker Compose files for 5.3.3 plus one cloudimg override - Every image already on the disk, so first boot and every later boot start the stack without pulling anything
- Docker CE from Docker's apt repository, its signing key pinned by fingerprint, running the stack under the systemd unit
community-health-toolkit.service - HTTPS on port 443 from
cht-nginxwith a certificate made for this instance on first boot, and port 80 redirecting to HTTPS - A per-instance CouchDB administrator password (the
medicaccount, which is also the CHT administrator), CouchDB cookie secret, CouchDB UUID and Erlang distribution cookie, all generated on first boot cht-set-hostname, a helper that serves CHT under your own DNS name and re-issues the certificate for it
Default user: ubuntu · Architecture: x86_64 · Root volume: 20 GiB gp3
Security posture
- No default login. Medic's example configuration uses a fixed CouchDB password and the upstream CouchDB image ships a fixed Erlang cookie. None of them is on this image: first boot generates a 32-character administrator password, a 64-character cookie secret, a UUID and a 48-character Erlang cookie on each instance, before any container starts, so no two instances share one. The Erlang cookie is read from a file only CouchDB's account can read, never from a command line.
medic/password,admin/adminand the other well-known logins are refused. - No data from the build. The CouchDB data directory is empty on the image and created on first boot. There is no build-time user, no demo data and no known password hash.
- Only
cht-nginxfaces the network. Docker publishes ports 80 and 443 and nothing else. CouchDB (5984), haproxy, the CHT API (5988), the search service and the Erlang ports sit on the internalcht-netnetwork with no host port. - The certificate is made on the instance. First boot creates a self-signed end-entity certificate (
CA:FALSE, server authentication only) for the instance's addresses; it is never baked into the image. Replace it with your own whenever you like (Step 8). - The hostname comes from configuration, never from a request. nginx answers only for the configured name and the instance's addresses (any other
Hostheader or TLS name gets its connection closed), hands CHT that fixed name, and replaces any client-suppliedX-Forwarded-For,X-Real-IPorForwardedheader with the real peer address. CHT's sign-in limiter therefore counts each client separately and a forged header cannot move it. - Two upstream library issues are blocked at the edge. CHT 5.3.3 ships body-parser and compression versions with published HIGH advisories (CVE-2026-111181 and CVE-2026-87776) and no fixed CHT release yet. nginx refuses compressed request bodies (
415) and always reads the API's response to its end, which closes both paths without changing Medic's images. - The containers cannot reach your private network or instance metadata. CHT can call URLs you configure (outbound push, SMS gateways). A firewall chain on the stack's Docker bridge rejects every new connection from the containers to link-local addresses (including the metadata service), carrier-grade NAT, RFC 1918 ranges, your VPC's CIDR blocks, loopback and reserved ranges, and the instance itself; only DNS to the instance's resolver and the public internet are allowed. The containers have no IPv6. The rules are in place before Docker starts any container, and Docker, containerd and the CHT units are also denied metadata by systemd
IPAddressDeny. - No core dumps. The containers and the CHT units run with core dumps disabled.
- IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances refuse token-less (IMDSv1) metadata requests. - Per-instance identity. SSH host keys, the machine ID, the TLS certificate and Docker's engine ID are generated fresh on every instance.
Medic's containers receive the CouchDB credentials as environment variables, as upstream designed them, so sudo docker inspect shows them to root. Nothing else on the instance does: the files that hold them are root-only and the Docker group is empty.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A computer with
cht-conf(Node.js) if you will deploy your own CHT configuration
Recommended instance type: m5.large (2 vCPUs, 8 GiB). With the stack running, a signed-in administrator and the guide's sample data, the instance uses about 1.6 GiB of its 8 GiB (CouchDB about 560 MiB, the search service about 270 MiB, the API about 200 MiB). Medic's guidance for production is 4 vCPUs, 8 GiB and 100 GB of SSD; grow the root volume as your data grows.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| Community Health Toolkit 5.3.3 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened EHRbase & GIS, Health and Research Platforms AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose Community Health Toolkit 5.3.3 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Security group: the listing's default group opens SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and 80 and 443 to the networks your health workers and administrators use.
- Choose your VPC and subnet, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH from your management address and HTTPS from your own network, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR ranges with your own:
SG=$(aws ec2 create-security-group --group-name community-health-toolkit \
--description "Community Health Toolkit" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr 203.0.113.0/24
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr 203.0.113.0/24
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=community-health-toolkit}]'
You do not need --metadata-options: the AMI already requires IMDSv2. If you want the address to survive a stop and start, associate an Elastic IP.
Step 2: Connect and find your login
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>
First boot takes about a minute. It generates the secrets, sets the hostname CHT is served as (the instance's public IP address until you choose a name), creates the TLS certificate for it, starts the stack, proves the administrator password signs in and that the well-known default logins do not, and writes /root/community-health-toolkit-credentials.txt (mode 0600, root). These are the entries in that file, then the names of every key it holds (the password itself is not shown here):
sudo grep -E '^CHT_(URL|ADMIN_USER)=' /root/community-health-toolkit-credentials.txt
sudo grep -oE '^[A-Z0-9_]+=' /root/community-health-toolkit-credentials.txt | tr -d '='
sudo stat -c '%a %U:%G' /root/community-health-toolkit-credentials.txt
CHT_URL=https://<public-ip>/
CHT_ADMIN_USER=medic
CHT_URL
CHT_ADMIN_USER
CHT_ADMIN_PASSWORD
CHT_TLS_SHA256_FINGERPRINT
600 root:root
| Value | What it is for |
|---|---|
CHT_URL |
The address of this instance's CHT web app |
CHT_ADMIN_USER |
medic, the CouchDB server administrator and CHT administrator |
CHT_ADMIN_PASSWORD |
Its password, generated on this instance |
CHT_TLS_SHA256_FINGERPRINT |
The SHA-256 fingerprint of this instance's certificate, to check the browser warning against |
To read the password, run sudo cat /root/community-health-toolkit-credentials.txt in your own session and store it in your password manager.
Step 3: Confirm CHT is running
The firewall units, Docker and the CHT stack are active, the six containers run from Medic's pinned images, only cht-nginx has host ports, and only sshd and ports 80 and 443 listen beyond loopback. The monitoring API reports the versions:
systemctl is-active docker.service cloudimg-cht-vpc-cidrs.service cloudimg-docker-imds-deny.service community-health-toolkit.service
sudo docker ps --format '{{.Names}} {{.Image}} {{.Ports}}' | sed -E 's/@sha256:[0-9a-f]+//' | sort
sudo ss -Htln | awk '{print $4}' | grep -vE '^(127\.|\[::1\])' | sort -u
curl -sk https://127.0.0.1/api/v2/monitoring | jq -c '{app: .version.app, couchdb: .version.couchdb}'
active
active
active
active
cht-api-1 public.ecr.aws/medic/cht-api:5.3.3 5988/tcp
cht-couchdb-1 public.ecr.aws/medic/cht-couchdb:5.3.3 4369/tcp, 5984/tcp, 9100/tcp
cht-haproxy-1 public.ecr.aws/medic/cht-haproxy:5.3.3 5984/tcp
cht-nginx-1 public.ecr.aws/medic/cht-nginx:5.3.3 0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp
cht-nouveau-1 public.ecr.aws/medic/cht-couchdb-nouveau:5.3.3 5987-5988/tcp
cht-sentinel-1 public.ecr.aws/medic/cht-sentinel:5.3.3
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
[::]:22
[::]:443
[::]:80
{"app":"5.3.3","couchdb":"3.5.3"}
Step 4: Prove the login and the defaults
Signed out, the databases and the admin API are refused, the well-known default logins are refused, plain HTTP redirects to HTTPS and an unknown Host header gets no answer. The last command runs the image's round-trip check: it signs in as the administrator, creates a health facility and a health worker, signs in as that worker, deletes both and confirms the deleted worker is refused:
B=https://127.0.0.1
curl -sk -o /dev/null -w '_all_dbs, signed out -> %{http_code}\n' -H 'Accept: application/json' "$B/_all_dbs"
curl -sk -o /dev/null -w 'users API, signed out -> %{http_code}\n' -H 'Accept: application/json' "$B/api/v1/users"
for pair in medic:password admin:admin; do
jq -nc --arg u "${pair%%:*}" --arg p "${pair#*:}" '{user:$u, password:$p}' |
curl -sk -o /dev/null -w "login ${pair%%:*}/${pair#*:} -> %{http_code}\n" -H 'Content-Type: application/json' --data-binary @- "$B/medic/login"
done
curl -s -o /dev/null -w 'http -> %{http_code} %{redirect_url}\n' http://127.0.0.1/medic/login
curl -sk -o /dev/null -w 'unknown Host -> %{http_code}\n' -H 'Host: evil.example' "$B/" || true
sleep 11
sudo community-health-toolkit-roundtrip.sh | tail -1
_all_dbs, signed out -> 401
users API, signed out -> 401
login medic/password -> 401
login admin/admin -> 401
http -> 301 https://<public-ip>/medic/login
unknown Host -> 000
CHT_ROUNDTRIP_OK
Step 5: Sign in to the CHT web app
Browse to CHT_URL (https://<public-ip>/). The certificate is self-signed, so your browser warns once; check that the fingerprint matches CHT_TLS_SHA256_FINGERPRINT and accept it for this host, or install your own certificate (Step 8). Sign in as medic with CHT_ADMIN_PASSWORD:

A wrong password is refused on the sign-in page and no session is created:

Because medic has no place of its own, CHT opens App Management after it signs in; click Application at the top right (or browse to https://<public-ip>/) for the web app. On a new instance the web app first shows Medic's Getting started tour of the default Maternal and Newborn Health app; Cancel and then Leave close it (it comes back until you finish it once):

Step 6: Add a place and a health worker
Add places and users in App Management (https://<public-ip>/admin/), or with the CHT API. This creates a district hospital with a health centre under it, creates a community health worker for the health centre with a password generated on the spot, signs in as that worker, and reads both back with the administrator account. The administrator password goes to curl in a root-only config file, so it never appears on a command line. CHT user names may contain only lower-case letters, digits, _ and -, and a name stays taken after its user is deleted:
B=https://127.0.0.1; A=$(mktemp); chmod 600 "$A"
sudo sed -n 's/^CHT_ADMIN_PASSWORD=//p' /root/community-health-toolkit-credentials.txt | { read -r P; printf 'user = "medic:%s"\n' "$P"; } > "$A"
PLACE=$(jq -nc '{name:"Riverside Health Centre", type:"health_center", parent:{name:"North District Hospital", type:"district_hospital"}}' |
curl -sk -K "$A" -H 'Content-Type: application/json' --data-binary @- "$B/api/v1/places" | jq -r '.id')
UPW="$(openssl rand -base64 18 | tr -dc 'A-Za-z0-9')Aa9"
jq -nc --arg p "$PLACE" --arg w "$UPW" '{username:"amina_chw", password:$w, roles:["chw"], place:$p, contact:{name:"Amina Okafor"}}' |
curl -sk -K "$A" -o /dev/null -w 'create user amina_chw -> %{http_code}\n' -H 'Content-Type: application/json' --data-binary @- "$B/api/v1/users"
jq -nc --arg w "$UPW" '{user:"amina_chw", password:$w}' |
curl -sk -o /dev/null -w 'amina_chw signs in -> %{http_code}\n' -H 'Content-Type: application/json' --data-binary @- "$B/medic/login"
curl -sk -K "$A" "$B/api/v1/users" | jq -r '.[] | select(.username=="amina_chw") | "user \(.username): role \(.type), place \(.place | map(.name) | join(", ")), contact \(.contact.name)"'
curl -sk -K "$A" "$B/medic/$PLACE" | jq -r '"place \(.name) (\(.type))"'
curl -sk -K "$A" "$B/medic/$(curl -sk -K "$A" "$B/medic/$PLACE" | jq -r '.parent._id')" | jq -r '"parent \(.name) (\(.type))"'
rm -f "$A"; unset UPW
create user amina_chw -> 200
amina_chw signs in -> 302
user amina_chw: role chw, place Riverside Health Centre, contact Amina Okafor
place Riverside Health Centre (health_center)
parent North District Hospital (district_hospital)
The People tab lists North District Hospital; open it and Riverside Health Centre is listed under Areas:

and App Management > Users lists the new health worker, with a place and a contact, next to the administrator:

Give each health worker their user name and password over a channel you trust; they sign in at CHT_URL from a browser or the CHT Android app.
Step 7: Check the security controls
The certificate was made for this instance and is a plain server certificate, Docker publishes only 80 and 443, the metadata service and a private address are refused from inside the CHT containers, the firewall chain on the stack's bridge rejects private destinations, the units are denied metadata, the containers restart on their own with rotated logs, and the secret files are closed to other users:
openssl s_client -connect 127.0.0.1:443 -servername 127.0.0.1 </dev/null 2>/dev/null |
openssl x509 -noout -subject -enddate -ext basicConstraints,extendedKeyUsage
for c in $(sudo docker ps --format '{{.Names}}' | sort); do echo "$c ports: $(sudo docker port "$c" | sort | paste -sd' ' -)"; done
for c in cht-api-1 cht-sentinel-1; do
sudo docker exec "$c" node -e 'const t=u=>fetch(u,{signal:AbortSignal.timeout(5000)}).then(r=>"reached "+r.status,e=>"blocked ("+((e.cause&&e.cause.code)||e.name)+")");Promise.all(["http://169.254.169.254/latest/meta-data/","http://10.0.0.1/","https://checkip.amazonaws.com/"].map(t)).then(r=>console.log(process.argv[1]+": metadata "+r[0]+", 10.0.0.1 "+r[1]+", public URL "+r[2]))' "$c"
done
sudo iptables -S DOCKER-USER
sudo iptables -S CLOUDIMG-CHT-EGRESS | grep -oE '\-d [0-9./]+ -j REJECT' | awk '{print $2}' | paste -sd' '
for u in docker community-health-toolkit; do echo "$u.service IPAddressDeny=$(systemctl show -p IPAddressDeny --value $u.service)"; done
sudo docker inspect --format '{{.Name}} restart={{.HostConfig.RestartPolicy.Name}} log={{.HostConfig.LogConfig.Type}}' $(sudo docker ps -q) | sort
sudo stat -c '%a %U:%G %n' /opt/cht/.env /opt/cht/secrets /opt/cht/tls /root/community-health-toolkit-credentials.txt
subject=CN = <public-ip>, O = cloudimg
notAfter=Feb 10 04:54:54 2029 GMT
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Extended Key Usage:
TLS Web Server Authentication
cht-api-1 ports:
cht-couchdb-1 ports:
cht-haproxy-1 ports:
cht-nginx-1 ports: 443/tcp -> 0.0.0.0:443 443/tcp -> [::]:443 80/tcp -> 0.0.0.0:80 80/tcp -> [::]:80
cht-nouveau-1 ports:
cht-sentinel-1 ports:
cht-api-1: metadata blocked (TimeoutError), 10.0.0.1 blocked (EHOSTUNREACH), public URL reached 200
cht-sentinel-1: metadata blocked (TimeoutError), 10.0.0.1 blocked (EHOSTUNREACH), public URL reached 200
-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
-A DOCKER-USER -i cht-br0 -j CLOUDIMG-CHT-EGRESS
0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.168.0.0/16 198.18.0.0/15 224.0.0.0/4 240.0.0.0/4 172.31.0.0/16
docker.service IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
community-health-toolkit.service IPAddressDeny=::ffff:169.254.169.254/128 fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32
/cht-api-1 restart=always log=local
/cht-couchdb-1 restart=always log=local
/cht-haproxy-1 restart=always log=local
/cht-nginx-1 restart=always log=local
/cht-nouveau-1 restart=always log=local
/cht-sentinel-1 restart=always log=local
600 root:root /opt/cht/.env
700 root:root /opt/cht/secrets
700 root:root /opt/cht/tls
600 root:root /root/community-health-toolkit-credentials.txt
From outside the instance only 22, 80 and 443 answer: CouchDB (5984 and 5986), haproxy, the API (5988) and the search service are refused even if you open those ports in the security group, because Docker never publishes them.
Sign-in throttling
CHT counts failed sign-ins: after 10 failures within 10 seconds it answers 429 Too Many Requests for 10 seconds. It counts them per client address (nginx hands CHT the real peer address, so one client cannot lock out another and a forged X-Forwarded-For header changes nothing), and also per user name. That second count is CHT's own design: anyone who can reach port 443 can make 10 wrong guesses for a known user name such as medic and lock that name out for 10 seconds at a time. To keep that from affecting you:
- restrict port 443 in the security group to the networks your health workers and administrators use, and
- for day-to-day administration, create your own administrator account with a user name that is not published anywhere (App Management > Users > Add user, role Administrator), and keep
medicfor recovery andcht-conf.
If you see 429 while signing in, wait 10 seconds and try again.
Step 8: Serve CHT under your own DNS name
Point a DNS record at the instance (an Elastic IP keeps the address stable), then tell CHT the name. The helper re-issues the self-signed certificate for it, updates nginx's accepted names and the host CHT is told it runs on, records the name in /etc/cloudimg/cht-hostname (kept across reboots), and recreates the cht-nginx container:
sudo cht-set-hostname cht.example.org
Until you set a name, the instance follows its own public IP address: if it comes back from a stop and start with a new address, the certificate and settings are updated at boot.
To use a certificate from your own CA or from Let's Encrypt instead, delete /opt/cht/tls/.cloudimg-managed (so the helper never touches the certificate again), replace /opt/cht/tls/cert.pem (the full chain) and /opt/cht/tls/key.pem (mode 0600, owner root), and run sudo docker restart cht-nginx-1. CHT's Android app and cht-conf both need a certificate they trust, so use a real one before you roll out to health workers.
Step 9: Deploy your own CHT configuration
The image runs Medic's default configuration. Build your project's forms, tasks, targets and contact hierarchy with cht-conf on your own computer, then deploy it to the instance as the administrator. With a certificate your computer trusts (Step 8):
npm install -g cht-conf
cht --url=https://medic:<CHT_ADMIN_PASSWORD>@cht.example.org
cht-conf reads the URL from its command line, so run it from your own computer, not the instance, and clear your shell history afterwards. See the CHT app developer guides for building a configuration.
Step 10: Restart, reboot and stop
The stack starts at every boot from the images on the disk, without pulling anything, and your data stays in /opt/cht/couchdb. To restart it, or reboot the instance:
sudo systemctl restart community-health-toolkit
sudo reboot
After a reboot, Step 3's checks report the same, and the place and user from Step 6 are still there.
Maintenance
- Logs:
sudo docker logs cht-api-1(API),sudo docker logs cht-sentinel-1,sudo docker logs cht-couchdb-1,sudo docker logs cht-nginx-1(HTTPS access) andsudo journalctl -u community-health-toolkit-firstboot.servicefor first boot. Container logs rotate at 20 MB, five files per container. - Restart:
sudo systemctl restart community-health-toolkit; the containers also restart on their own if one exits. - Backups: CouchDB's data is in
/opt/cht/couchdb. Take EBS snapshots of the root volume, or use CouchDB replication to another server. Keep/opt/cht/.env(root only) with your backups: it holds the administrator password and secrets the data was written with. - Operating system updates: Ubuntu's unattended security upgrades stay on, so Docker and the rest of the OS keep receiving patches. Apply the rest with
sudo apt update && sudo apt upgradein a maintenance window. - Upgrading CHT: the images are pinned by digest and the stack never pulls on its own. To upgrade deliberately, read Medic's release notes, back up, download Medic's compose files for the new release (from the same build server as the 5.3.3 ones below), pin each
image:line to its digest, put them in/opt/cht/compose, pull the images and runsudo systemctl restart community-health-toolkit. Keep/opt/cht/compose/cht-cloudimg.yml: it mounts the per-instance Erlang cookie file and avm.argswithout the image's shared cookie, the certificate, thecht-netbridge name the firewall chain is keyed on and the core-dump limits. Never publish another port and never use host networking, which would bypass nginx and the firewall chain.
Troubleshooting
The sign-in page says the user name or password is wrong. Use medic and CHT_ADMIN_PASSWORD from /root/community-health-toolkit-credentials.txt exactly, over https://. After 10 failed attempts in 10 seconds CHT answers 429 for 10 seconds; wait and try again (see Sign-in throttling in Step 7).
The page does not load, or the connection is closed. nginx answers only for the instance's address and the name you set with cht-set-hostname. Browse to the exact CHT_URL, or set the name you use.
The stack did not start. sudo journalctl -u community-health-toolkit-firstboot.service --no-pager | tail -20 on a new instance, and sudo journalctl -u community-health-toolkit --no-pager | tail -20 and sudo docker ps -a after that.
An outbound push or SMS gateway to an address in your VPC fails. The containers are refused private, link-local and carrier-grade NAT addresses by design, so an integration must use a public HTTPS endpoint.
Support
cloudimg provides 24/7 support for this image by email and live chat: launching and connecting, users and places, TLS and hostnames, configuration deployment and upgrades.
The Community Health Toolkit Core Framework is developed by Medic and contributors and is released under the GNU Affero General Public License v3.0 (AGPL-3.0). This image runs Medic's release images unmodified; the complete corresponding source for the version on the image is the 5.3.3 tag of medic/cht-core, and the stack is started with Medic's own Compose files for that release, cht-core.yml and cht-couchdb.yml, with the image lines pinned to their digests. The licence text and this source notice ship on the instance in /usr/share/doc/community-health-toolkit/. CouchDB is released by the Apache Software Foundation under the Apache License 2.0. Community Health Toolkit and CHT are names of the open source project stewarded by Medic; cloudimg is not affiliated with, endorsed by or sponsored by Medic. Ubuntu is a registered trademark of Canonical Ltd. Docker is a trademark of Docker, Inc. All other product and company names are trademarks or registered trademarks of their respective holders.