GNU Health 5.0.7 on AWS User Guide
Overview
This guide covers launching and using the GNU Health 5.0.7 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg GIS, health and research applications listing on AWS Marketplace; you choose it as the software version when you launch.
GNU Health is the free and libre hospital and health information system of GNU Solidario, an official GNU package. Its Hospital Information System (HIS) keeps patients and their health records, appointments, health professionals and institutions. It runs on the Tryton application framework: the Tryton server (trytond) stores everything in PostgreSQL, and the sao web client is the browser interface.
The cloudimg image is a hardened, ready-to-use GNU Health server:
- HTTPS from the first boot, with a certificate generated on your instance (an end-entity certificate, not a certificate authority), TLS 1.2 and 1.3 only. Port 80 only redirects to HTTPS, so a password never crosses the network in clear text. Requests for a host name the instance does not serve are dropped.
- nginx in front, Tryton behind it. nginx is the only public web listener. It serves the sao web client as static files and passes the JSON-RPC calls to the Tryton server, which runs under gunicorn on
127.0.0.1:8000only. nginx sets the client address from the real connection (a forgedX-Forwarded-FororX-Real-IPis overwritten), limits sign-ins and API calls per IPv4 address or IPv6 /64, and adds HSTS, a Content Security Policy,Referrer-Policy: same-originandX-Frame-Options: SAMEORIGIN. - No default account. First boot creates the database and generates the password of the Tryton
adminuser, and writes it to a root-only file. The image ships no password and no known account. The GNU Health core module brings five demo users (demo_doctor,demo_nurses,demo_frontdesk,demo_back_office,demo_social_worker); on this image they are inactive and have no password, so nobody can sign in with them. - Database management switched off. The sign-in dialog does not list databases, and Tryton 7.0 has no remote call to create, drop, dump or restore a database. Nobody can create a database or replace yours from the network.
- PostgreSQL on its local socket only. PostgreSQL 16 listens on no TCP port at all. The Tryton server connects over the Unix socket with peer authentication, so there is no database password to steal.
- An outbound block. The
gnuhealthaccount (and nginx) are refused, by nftables and by systemd, every connection to the EC2 metadata service, to private and reserved ranges, to this instance's own VPC ranges and to IPv6. A fresh instance cannot be turned into a scanner of your network. You open a mail relay in your VPC with one line (Step 11). - An empty database. The image ships with no database at all: first boot creates an empty
gnuhealthdatabase with the GNU Health core module. No patient, no demo database and no test record is on the image. The build created and deleted synthetic records only, and checked that none remained. - An unprivileged service. The Tryton server runs as the
gnuhealthaccount, which has no shell, no password and no sudo rights, under systemd withNoNewPrivileges, a read-only operating system and core dumps off. The web client's files are owned by root, and the Tryton server cannot change them. Attachments are stored in/var/lib/gnuhealth/filestore, which nginx never serves. - Built from pinned releases. GNU Health's core module, Tryton and every Python package are installed from SHA-256-pinned releases; the core is byte-identical to the GNU-signed GNU Health HIS 5.0.7 release. The web client and its JavaScript libraries are the pinned upstream release files. nginx, PostgreSQL and Python come from the Ubuntu 24.04 main archive and are patched by Ubuntu's security updates.
What is on the image: GNU Health HIS 5.0.7 (core module), Tryton 7.0 LTS (trytond and the sao web client), gunicorn, PostgreSQL 16 and nginx from Ubuntu 24.04, cloud-init and the AWS Systems Manager agent.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet.
- A security group that allows TCP 22 (SSH), TCP 443 (the web client) and TCP 80 (redirects to HTTPS) from the networks your users are on. Do not open 8000 or 5432: the Tryton server listens on the loopback address only and PostgreSQL on no TCP port.
- IMDSv2 enabled (the default for this AMI). The instance reads its own VPC ranges from the metadata service at every boot; with the metadata service switched off, the Tryton server does not start (see Step 12).
- For production, a DNS name you control (for example
his.example.com) and, ideally, a certificate from a certificate authority. Until then the instance uses its EC2 public DNS name with its own self-signed certificate. - Health records are sensitive data. Run the instance in a private subnet or behind a VPN where you can, keep 443 open only to your clinical networks, and follow your organisation's rules for patient data.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick GNU Health 5.0.7 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (or larger for many concurrent users), your key pair, and a security group as above.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --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=gnu-health}]'
First boot takes about a minute after the instance is running: it creates the PostgreSQL cluster and the gnuhealth database, generates the administrator password and the certificate, then starts the Tryton server and nginx. The instance is named by its EC2 public DNS name (or its public IP address, or its private name or address when it has no public one), and it also answers on its other addresses.
Step 2: Connect to your instance
| Operating system | SSH user |
|---|---|
| Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
The login message shows the web address, the database name and where the password is.
Step 3: Check the services
systemctl is-active cloudimg-gnuhealth-metadata postgresql@16-main gnuhealth nginx
active
active
active
active
The listeners: SSH, HTTP and HTTPS for everyone, and the Tryton server on the loopback address. PostgreSQL has no TCP listener:
sudo ss -Htln | awk '{print $4}' | sort -u
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:8000
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80
(127.0.0.53 and 127.0.0.54 are Ubuntu's local DNS resolver.)
Step 4: Read the address, the password and the certificate fingerprint
Everything first boot generated is in /root/gnuhealth-credentials.txt (root only). The web address, the host name, the database and the certificate's SHA-256 fingerprint:
sudo grep -E '^GNUHEALTH_(URL|HOST|DATABASE|ADMIN_USERNAME|TLS_SHA256)=' /root/gnuhealth-credentials.txt
GNUHEALTH_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
GNUHEALTH_HOST=ec2-203-0-113-10.compute-1.amazonaws.com
GNUHEALTH_DATABASE=gnuhealth
GNUHEALTH_ADMIN_USERNAME=admin
GNUHEALTH_TLS_SHA256=3B:9E:41:07:C2:5D:A8:16:F0:4C:72:E9:1B:D3:68:A5:0F:27:94:BC:E6:13:5A:D8:40:7F:C1:2E:96:B8:0D:5C
The same file holds GNUHEALTH_ADMIN_PASSWORD. Print only that line when you need it, with sudo grep '^GNUHEALTH_ADMIN_PASSWORD=' /root/gnuhealth-credentials.txt, and keep the password out of tickets and chat. The certificate is self-signed for this instance: when a browser asks you to trust it, compare its SHA-256 fingerprint with GNUHEALTH_TLS_SHA256.
A quick check from the instance itself (/healthz answers with the Tryton server's version, without a password):
curl -sk https://127.0.0.1/healthz
{"id":0,"result":"7.0.61"}
Step 5: Sign in to the web client
Open GNUHEALTH_URL in a browser and accept this instance's certificate after comparing its fingerprint. The sign-in dialog asks for:
- Database:
gnuhealth. Type it: the dialog does not list databases on this image. - User name:
admin, then Login, and the passwordGNUHEALTH_ADMIN_PASSWORDwhen it is asked for.

On the first sign-in Tryton opens the Module Configuration wizard. Choose OK to go through the setup wizards (for example your company and its currency) or Cancel to do it later. The menu on the left holds Health, Party, Companies and Administration.

A wrong password, an unknown user or a request without a session is refused. Tryton allows 5 failed sign-ins for a user name and 300 for a client network before it answers "Too many requests", with a growing delay; nginx also limits sign-in calls to 30 a minute (bursts of 20) and all calls to 20 a second (bursts of 100) per IPv4 address or IPv6 /64.
Step 6: Patients and appointments
GNU Health keeps a person as a party and a patient as a party marked as a patient. Under Health open Patients to register patients and their health records, and Appointments to book them. The image ships with an empty database; the screenshots below show a synthetic patient made for this guide (an invented name, no patient data), which was deleted afterwards.


Attachments (scanned documents, images) are stored in the database's file store, /var/lib/gnuhealth/filestore, which only the gnuhealth account can read and which nginx never serves.
Step 7: Users and access rights
Create a user for each person under Administration > Users and give each the groups they need (the GNU Health groups such as doctor, nurse and front desk come with the core module). Use your own users for daily work and keep admin for administration.
The five demo users of the core module are inactive and have no password on this image. Leave them inactive, or delete them under Administration > Users (show inactive records with the filter).
Tryton 7.0.60 fixed security issue 15205: a wizard that names no model and no group could be run by any signed-in user. Since that fix (this image has 7.0.61), such a wizard runs only for members of the Administration group or of a group you add to the wizard. If a user cannot open such a wizard, add the user's group to the wizard's Groups under Administration > User Interface > Actions > Wizards.
Step 8: Check the whole instance
The self-test proves the web client and its Content Security Policy over HTTPS, that the database list is refused, that an anonymous call and a wrong password are refused, that this instance's admin signs in and reads its preferences, and that signing out ends the session:
sudo gnuhealth-selftest
gnuhealth-selftest: sao 200 with CSP, db.list 403, anonymous model call refused, wrong password refused, admin sign-in + preferences 200, logout revokes the session
GNUHEALTH_SELFTEST_OK
Step 9: Change the administrator password
sudo gnuhealth-admin
This gives admin a new random password and stores it in /root/gnuhealth-credentials.txt as GNUHEALTH_ADMIN_PASSWORD; it is never printed and never on a command line. You can also change it in the web client under the user's Preferences.
Step 10: Use your own domain and certificate
Point a DNS name at the instance, then make it the instance's name. The command writes the nginx host names (requests for any other name are dropped), generates a new self-signed certificate for the names and restarts nginx:
sudo gnuhealth-set-domain his.example.com
To use a certificate from a certificate authority instead, pass the full chain and its key; the command checks that the key matches and that the certificate covers the name:
sudo gnuhealth-set-domain --tls-cert /path/fullchain.pem --tls-key /path/privkey.pem his.example.com
If the instance's public address changes after a stop and start, run gnuhealth-set-domain again with the new name or address.
Step 11: Send email (a relay in your VPC)
Tryton sends mail only to the relay in its configuration, /etc/gnuhealth/trytond.conf ([email] uri, smtp://localhost:25 by default, where no relay runs). A relay on a public address needs only that setting. A relay on a private address or in this VPC is refused by the outbound block until you open it, one address or CIDR per line:
echo '10.0.5.25' | sudo tee -a /etc/cloudimg/gnuhealth-egress-allow
sudo systemctl restart cloudimg-gnuhealth-metadata gnuhealth
The EC2 metadata service can never be opened.
Step 12: The VPC ranges and a fail-closed start
The outbound block refuses the Tryton server every private address and this instance's own VPC ranges, which the instance reads at every boot:
sudo cat /run/cloudimg-gnuhealth/vpc-cidrs
# written by gnuhealth-metadata at 2026-11-11T03:56:30Z
source imds
4 172.31.0.0/16
If the Tryton server does not start after a reboot. The block is fail closed: the instance reads its VPC ranges from EC2 metadata at every boot (with retries), then from cloud-init's copy, then, on later boots only, from the last list this instance recorded. If none of them gives the ranges (for example after the metadata service was switched off on the instance), the Tryton server stays down and nginx with it, and journalctl -u cloudimg-gnuhealth-metadata -u gnuhealth says so. Turn IMDSv2 back on (aws ec2 modify-instance-metadata-options --instance-id <id> --http-endpoint enabled --http-tokens required); the metadata service retries on its own and starts the Tryton server and nginx once it has the ranges.
Step 13: Backups
Everything is in the gnuhealth PostgreSQL database and the file store. A database dump, as the gnuhealth account (peer authentication, no password):
sudo -u gnuhealth pg_dump -Fc gnuhealth > /var/tmp/gnuhealth-$(date +%F).dump
sudo tar -C /var/lib/gnuhealth -czf /var/tmp/gnuhealth-filestore-$(date +%F).tar.gz filestore
Copy both off the instance, or take EBS snapshots or use AWS Backup, and treat the backups as patient data. To grow the disk, modify the EBS volume, then sudo growpart /dev/nvme0n1 1 && sudo resize2fs /dev/nvme0n1p1.
Step 14: Versions, licences and source
| Component | Version | Licence |
|---|---|---|
GNU Health HIS (core module gnuhealth) |
5.0.7 release (module 5.0.6) | GPL-3.0-or-later |
gnuhealth-control (GNU Health's command-line helper, from PyPI, SHA-256-pinned) |
5.0.4 | GPL-3.0-or-later |
Tryton server trytond |
7.0.61 | GPL-3.0-or-later |
| Tryton modules party, company, country, currency, product | 7.0 series | GPL-3.0-or-later |
| sao web client | 7.0.61 | GPL-3.0-or-later |
| gunicorn | 27.0.0 | MIT |
| PostgreSQL (Ubuntu 24.04) | 16 | PostgreSQL |
| nginx (Ubuntu 24.04) | 1.24.0 | BSD-2-Clause |
cat /usr/share/doc/cloudimg-gnuhealth/SOURCE.txt
GNU Health and Tryton are free software under the GNU General Public License version 3 or later; the licence text is /usr/share/doc/cloudimg-gnuhealth/COPYING-GPL-3.0.txt. SOURCE.txt names the GNU-signed release this image matches and the SHA-256 of the corresponding source of every GPL package; dependency-licences.txt lists the licence of every Python package and sao-third-party.txt the web client's JavaScript libraries with their licences as published (gettext.js 0.7.0 declares MIT in its package metadata only). cloudimg changes no GNU Health or Tryton source file.
The Content Security Policy allows 'unsafe-eval' for scripts because the sao web client evaluates Tryton's domain expressions in the browser; it allows no other inline script than the two blocks of sao's own start page, by their hashes. The web client ships Bootstrap 3.4.1, which upstream no longer maintains: its published advisories CVE-2024-6485 and CVE-2025-1647 (cross-site scripting through the button loading text and the popover/tooltip sanitizer) have no fix in Bootstrap 3. They are limited here by the Content Security Policy, which refuses inline scripts and scripts from any other site.
Ubuntu's unattended upgrades keep the operating system, PostgreSQL and nginx patched. GNU Health and Tryton do not update automatically: the cloudimg listing adds a new version when the projects release one. To keep your work across a move to a new instance, restore the database dump and the file store of Step 13.
GNU Health is developed by GNU Solidario and Tryton by the Tryton project. cloudimg packages and configures them and is not affiliated with or endorsed by either project. GNU Health on this image is not a certified medical device; check your own regulatory requirements before clinical use.
Support
cloudimg provides support for this image by email at support@cloudimg.co.uk, 24 hours a day, 7 days a week. Tell us the AMI ID and Region, and include the output of sudo gnuhealth-selftest and systemctl status gnuhealth nginx postgresql@16-main cloudimg-gnuhealth-metadata. For questions about GNU Health itself, see the GNU Health documentation and the GNU Health mailing lists.