GNU Health 5.0.7
Overview
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.
This guide covers launching and using the SecureAnyCloud GNU Health 5.0.7 on Ubuntu 24.04 AMI on Amazon EC2. The AMI is one version of the SecureAnyCloud GIS, health and research applications listing on AWS Marketplace. You choose it as the software version when you launch.
What you get 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. Both nftables and systemd refuse the
gnuhealthaccount (and nginx) 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 can 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
Before you deploy this image, make sure you have:
- 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 has 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. Where you can, run the instance in a private subnet or behind a VPN, keep port 443 open only to your clinical networks, and follow your organization's rules for patient data.
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, select your Region, and choose Launch through EC2.
Choose m5.large (or larger for many concurrent users), your key pair, and a security group as described above.
Alternatively, launch 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). It also answers on its other addresses.
Connect to your instance
Connect over SSH. Operating system: Ubuntu 24.04. SSH user: ubuntu.
ssh -i <your-key.pem> ubuntu@<public-ip>The login message shows the web address, the database name and the location of the password.
Check the services
systemctl is-active sac-gnuhealth-metadata postgresql@16-main gnuhealth nginxactive active active activeList the listeners. SSH, HTTP and HTTPS listen for everyone, and the Tryton server listens on the loopback address. PostgreSQL has no TCP listener:
sudo ss -Htln | awk '{print $4}' | sort -u0.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 [::]:80The addresses 127.0.0.53 and 127.0.0.54 belong to Ubuntu's local DNS resolver.
Read the address, the password and the certificate fingerprint
First boot writes everything it generated to /root/gnuhealth-credentials.txt, which only root can read. Show 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.txtGNUHEALTH_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:5CThe 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.
Run a quick check from the instance itself. The /healthz endpoint answers with the Tryton server's version, without a password:
curl -sk https://127.0.0.1/healthz{"id":0,"result":"7.0.61"}Sign in to the web client
Open GNUHEALTH_URL in a browser and accept this instance's certificate after comparing its fingerprint. Fill in the sign-in dialog:
- Database:
gnuhealth. Type it, because the dialog does not list databases on this image. - User name:
admin. Select Login, then enter the passwordGNUHEALTH_ADMIN_PASSWORDwhen asked.
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.
Tryton refuses a wrong password, an unknown user or a request without a session. It 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.
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 open 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.
Users and access rights
Create a user for each person under Administration > Users and give each user 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. Use the filter to show inactive records.
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.
Check the whole instance
Run the self-test. It proves the following:
- The web client and its Content Security Policy are served over HTTPS.
- The database list is refused.
- An anonymous call and a wrong password are refused.
- This instance's
adminsigns in and reads its preferences. - Signing out ends the session.
sudo gnuhealth-selftestgnuhealth-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_OKChange the administrator password
sudo gnuhealth-adminThis command gives admin a new random password and stores it in /root/gnuhealth-credentials.txt as GNUHEALTH_ADMIN_PASSWORD. The password is never printed and never appears on a command line. You can also change it in the web client under the user's Preferences.
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.comTo 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.comIf the instance's public address changes after a stop and start, run gnuhealth-set-domain again with the new name or address.
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.
The outbound block refuses a relay on a private address or in this VPC until you open it. Add one address or CIDR per line:
echo '10.0.5.25' | sudo tee -a /etc/sac/gnuhealth-egress-allow
sudo systemctl restart sac-gnuhealth-metadata gnuhealthThe EC2 metadata service can never be opened.
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. Inspect the block:
sudo cat /run/sac-gnuhealth/vpc-cidrs# written by gnuhealth-metadata at 2026-11-11T03:56:30Z source imds 4 172.31.0.0/16If the Tryton server does not start after a reboot. The block is fail-closed. At every boot, the instance reads its VPC ranges from EC2 metadata (with retries), then from cloud-init's copy, then, on later boots only, from the last list this instance recorded.
If none of these sources gives the ranges (for example, after the metadata service was switched off on the instance), the Tryton server stays down and nginx with it. The command journalctl -u sac-gnuhealth-metadata -u gnuhealth shows the reason.
To recover, turn IMDSv2 back on with 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.
Backups
All your data is in the gnuhealth PostgreSQL database and the file store. Dump the database as the gnuhealth account, which uses peer authentication and needs 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 filestoreCopy both off the instance, or take EBS snapshots or use AWS Backup. Treat the backups as patient data. To grow the disk, modify the EBS volume, then run sudo growpart /dev/nvme0n1 1 && sudo resize2fs /dev/nvme0n1p1.
Versions, licenses and source
Component, version and license:
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/sac-gnuhealth/SOURCE.txtGNU Health and Tryton are free software under the GNU General Public License version 3 or later. The license text is at /usr/share/doc/sac-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 license of every Python package. sao-third-party.txt lists the web client's JavaScript libraries with their licenses as published (gettext.js 0.7.0 declares MIT in its package metadata only). SecureAnyCloud 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, identified 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. The Content Security Policy limits them here, because it 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 SecureAnyCloud 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 from Step 13.
GNU Health is developed by GNU Solidario and Tryton by the Tryton project. SecureAnyCloud 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
SecureAnyCloud provides support for this image by email at sales@secureanycloud.com, 24 hours a day, 7 days a week. In your message, include the AMI ID and Region, plus the output of sudo gnuhealth-selftest and systemctl status gnuhealth nginx postgresql@16-main sac-gnuhealth-metadata.
For questions about GNU Health itself, see the GNU Health documentation and the GNU Health mailing lists.
Deploy on AWS
Launch GNU Health 5.0.7 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