1. Before you start
What the server needs, the accounts this guide uses, and the check to make before installing. This guide installs HDS-Explorer 1.0.0 build 7 and was tested with it.
1.1 What gets installed
The server is a web application. Data managers use its console in a browser. The tablet app is an Android app that field workers use, and that sends its data to the server.
The server is three programs on one machine:
| Program | Version |
|---|---|
| Java (JDK) | 17 |
| Apache Tomcat | 9.0 |
| MySQL | 8.4 LTS |
Use exactly these major versions. Java 21, Tomcat 10 or 11, and MySQL 8.0 or 9.x are not supported.
1.2 The server machine
| Minimum | Recommended | |
|---|---|---|
| Operating system | Ubuntu Server or Desktop 26.04 LTS, 64-bit | |
| Processor | x64 or ARM64, 2 cores | 4 cores |
| Memory | 4 GB | 8 GB |
| Disk | 40 GB free | 100 GB free |
| Network | A fixed address on the network the tablets use |
The tablets are given the server's address once and keep it. If the address changes, every tablet has to be told again.
The tablets talk to the server over plain HTTP, for example
http://192.168.1.10:8081/hds-explorer-server. Put the server on a network you trust. Never open its
port to the internet. The tablets use the local build of the app, local-4.apk.
1.3 The tablets
Android 6.0 or newer, 2 GB free, and Wi-Fi or mobile data that reaches the server. The tablet app,
local-4.apk (1.0.0 build 4-local, 34 MB), is installed in Using HDS-Explorer, chapter C.
1.4 Accounts and passwords
Every site uses the same accounts and passwords for the test, so any step can be repeated anywhere.
| Account | Where | User name | Password |
|---|---|---|---|
| MySQL administrator | MySQL | root |
Hdsx-root-1 |
| The server's database account | MySQL, database hds_explorer_db |
hdsx_app |
Hdsx-app-1 |
| Read-only database account | MySQL | hdsx_read |
Hdsx-read-1 |
| First administrator | Console, first sign-in only | admin |
Change-me-1 |
| Administrator | Console | admin |
Hdsx-admin-1 (set at the first sign-in) |
| Data manager | Console | dm01 |
Hdsx-dm01-1 |
| Supervisor | Console and tablet | sv01 |
Hdsx-sv01-1 |
| Field worker | Tablet | fw01 |
Hdsx-fw01-1 |
Before the server holds real data, change every one of these passwords.
1.5 Ports
| Port | Used by | Open to |
|---|---|---|
| 8081 | Tomcat: the console and the tablets | The site network |
| 3307 | MySQL | This machine only |
1.6 Check the machine is empty
Run:
systemctl list-units --type=service --all | grep -Ei 'tomcat|mysql|mariadb'
sudo ss -ltnp | grep -E ':(3306|3307|8080|8081) '
java -version
The first two print nothing, and java -version says Command 'java' not found. If anything else
shows, another system uses this machine: install HDS-Explorer on a machine of its own.
You also need an account that can use sudo, internet access on the machine, and its fixed address.
2. Install
Installs Java 17, MySQL 8.4 and Tomcat 9, creates the database, and puts the HDS-Explorer server into
Tomcat. Run every command in a terminal, as the account that can use sudo.
2.1 Java 17
Installs Java and the tools the guide uses (curl, unzip, jq):
sudo apt update
sudo apt install -y openjdk-17-jdk-headless curl unzip jq
java -version
The first line says openjdk version "17.0.20.1" (or a newer 17).
2.2 MySQL 8.4
Installs MySQL:
sudo apt install -y mysql-server
mysql --version
It says Ver 8.4.11-0ubuntu0.26.04.1 or a newer 8.4.
Creates MySQL's settings file (port 3307, this machine only, Amharic-safe text, Ethiopian time) and
restarts MySQL. Use your site's offset from UTC for default-time-zone if it is elsewhere:
sudo tee /etc/mysql/mysql.conf.d/hdsx.cnf > /dev/null <<'EOF'
[mysqld]
port=3307
bind-address=127.0.0.1
mysqlx=OFF
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default-time-zone=+03:00
max_allowed_packet=64M
EOF
sudo systemctl restart mysql
Gives the MySQL root account the password of chapter 1.4:
sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'Hdsx-root-1';"
Checks it:
mysql -h 127.0.0.1 -P 3307 -u root -pHdsx-root-1 -e "SELECT VERSION(), @@port, @@bind_address, @@collation_server, @@time_zone;"
It prints 8.4.11-0ubuntu0.26.04.1, 3307, 127.0.0.1, utf8mb4_unicode_ci and +03:00, after a
warning about a password on the command line. Every mysql command in this guide names
-h 127.0.0.1 -P 3307.
2.3 Tomcat 9
Tomcat runs as its own user, hdsx, with no login:
sudo useradd --system --home-dir /opt/tomcat9 --shell /usr/sbin/nologin hdsx
Downloads Tomcat 9.0.123 and checks it:
cd /tmp
curl -fLO https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.123/bin/apache-tomcat-9.0.123.tar.gz
curl -fLO https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.123/bin/apache-tomcat-9.0.123.tar.gz.sha512
sha512sum -c apache-tomcat-9.0.123.tar.gz.sha512
The last line says apache-tomcat-9.0.123.tar.gz: OK. Do not continue if it says anything else.
If 9.0.123 is no longer on dlcdn.apache.org, use the same file from
https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.123/bin/.
Unpacks Tomcat and removes its sample pages:
sudo mkdir -p /opt/tomcat9
sudo tar -xzf apache-tomcat-9.0.123.tar.gz -C /opt/tomcat9 --strip-components=1
sudo rm -rf /opt/tomcat9/webapps/ROOT /opt/tomcat9/webapps/docs /opt/tomcat9/webapps/examples /opt/tomcat9/webapps/manager /opt/tomcat9/webapps/host-manager
Sets Tomcat's port to 8081, switches off its shutdown port, and keeps its Java settings out of its log:
sudo sed -i \
-e 's/port="8080"/port="8081"/' \
-e 's/port="8005"/port="-1"/' \
-e 's|<Listener className="org.apache.catalina.startup.VersionLoggerListener" />|<Listener className="org.apache.catalina.startup.VersionLoggerListener" logArgs="false" />|' \
/opt/tomcat9/conf/server.xml
sudo grep -E 'Server port=|Connector port="8081"|VersionLoggerListener' /opt/tomcat9/conf/server.xml
sudo chown -R hdsx:hdsx /opt/tomcat9
The grep shows Server port="-1", Connector port="8081" and VersionLoggerListener with
logArgs="false".
Creates the Tomcat service (use your site's time zone for -Duser.timezone if it is elsewhere):
sudo tee /etc/systemd/system/tomcat9.service > /dev/null <<EOF
[Unit]
Description=Apache Tomcat 9 (HDS-Explorer)
After=network.target mysql.service
Requires=mysql.service
[Service]
Type=simple
User=hdsx
Group=hdsx
Environment=JAVA_HOME=/usr/lib/jvm/java-17-openjdk-$(dpkg --print-architecture)
Environment=CATALINA_HOME=/opt/tomcat9
Environment=CATALINA_BASE=/opt/tomcat9
Environment="CATALINA_OPTS=-Xms512m -Xmx2048m -Dfile.encoding=UTF-8 -Duser.timezone=Africa/Addis_Ababa -Djava.awt.headless=true"
ExecStart=/opt/tomcat9/bin/catalina.sh run
SuccessExitStatus=143
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
grep JAVA_HOME /etc/systemd/system/tomcat9.service
The grep shows JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 (x64) or -arm64 (ARM).
Starts Tomcat, now and at every boot:
sudo systemctl daemon-reload
sudo systemctl enable --now tomcat9
systemctl is-active tomcat9
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8081/
is-active says active and curl prints 404: Tomcat answers and has nothing to serve yet. If it
prints 000, wait ten seconds and run it again.
2.4 The clock
The server stamps every record with its own time. Check:
date
chronyc tracking | grep 'System time'
date shows the right time. If it is minutes off, set it at once with sudo chronyc makestep.
2.5 Firewall
Check:
sudo ufw status
If it says inactive, leave it. If it says active, let the site network reach port 8081 (replace
192.168.1.0/24 with your site network):
sudo ufw allow from 192.168.1.0/24 to any port 8081 proto tcp
2.6 Download the server
One small file, server.json, names the current server zip and release pack. The files are:
| File | Size |
|---|---|
| hds-explorer-server-1.0.0-build7.zip | 148 MB |
| release-pack-standard-2.zip | The standard forms, imported in Using HDS-Explorer, chapter B |
Downloads both into /opt/hdsx/downloads and checks each against server.json:
sudo mkdir -p /opt/hdsx/downloads
sudo chown "$USER" /opt/hdsx/downloads
cd /opt/hdsx/downloads
curl -fLO https://apps.techallyconsult.com/hds-explorer/server/server.json
for part in server releasePack; do
file=$(jq -r ".$part.file" server.json)
curl -fL -o "$file" "$(jq -r ".$part.url" server.json)"
echo "$(jq -r ".$part.sha256" server.json) $file" | sha256sum -c
done
Each file prints OK. A FAILED means the download was cut: run the loop again.
2.7 Create the database
Opens MySQL as root:
mysql -h 127.0.0.1 -P 3307 -u root -pHdsx-root-1
At the mysql> prompt, creates the database and the server's account:
CREATE DATABASE hds_explorer_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'hdsx_app'@'localhost' IDENTIFIED BY 'Hdsx-app-1';
GRANT ALL PRIVILEGES ON hds_explorer_db.* TO 'hdsx_app'@'localhost';
SELECT default_collation_name FROM information_schema.schemata WHERE schema_name = 'hds_explorer_db';
exit
The SELECT prints utf8mb4_unicode_ci.
2.8 Put the server into Tomcat
Stops Tomcat and unpacks the server into it:
sudo systemctl stop tomcat9
sudo unzip -q /opt/hdsx/downloads/hds-explorer-server-1.0.0-build7.zip -d /opt/tomcat9/webapps
sudo chown -R hdsx:hdsx /opt/tomcat9/webapps/hds-explorer-server
sudo ls /opt/tomcat9/webapps/hds-explorer-server/WEB-INF/classes/app-config.yml
The last line prints the file's name back. The server's address is
http://<server>:8081/hds-explorer-server.
2.9 The server's settings
Writes the settings to a file only root can read, and makes Tomcat's service read it:
sudo mkdir -p /etc/hds-explorer /opt/hdsx/data
sudo chown hdsx:hdsx /opt/hdsx/data
sudo tee /etc/hds-explorer/server.env > /dev/null <<'EOF'
PROD_DATABASE_URL=jdbc:mysql://127.0.0.1:3307/hds_explorer_db?sslMode=REQUIRED&zeroDateTimeBehavior=convertToNull
PROD_DATABASE_USER=hdsx_app
PROD_DATABASE_PASS=Hdsx-app-1
PROD_ADMIN_USERNAME=admin
PROD_ADMIN_PASSWORD=Change-me-1
HDS_EXPLORER_SYSTEM_PATH=/opt/hdsx/data
EOF
sudo chmod 600 /etc/hds-explorer/server.env
sudo mkdir -p /etc/systemd/system/tomcat9.service.d
sudo tee /etc/systemd/system/tomcat9.service.d/hdsx.conf > /dev/null <<'EOF'
[Service]
EnvironmentFile=/etc/hds-explorer/server.env
EOF
sudo systemctl daemon-reload
systemctl show tomcat9 -p EnvironmentFiles
The last line shows EnvironmentFiles=/etc/hds-explorer/server.env (ignore_errors=no).
To change a setting later, edit the file (sudo nano /etc/hds-explorer/server.env) and restart Tomcat.
Next: chapter 3, the first start.
3. First start and sign-in
Starts the server, checks it is healthy, and signs in as the administrator. Chapter 2 must be done.
3.1 First start
The first start builds the database and takes 1 to 5 minutes. Starts Tomcat:
sudo systemctl start tomcat9
Checks the health address, every half minute, until it answers:
curl -s http://localhost:8081/hds-explorer-server/health; echo
While the server is starting it prints nothing. When ready it prints:
{"status":"UP","checks":{"application":"UP","database":"UP","storage":"UP"},"version":"1.0.0 build 7"}
| It says | It means |
|---|---|
database not UP |
The settings of 2.9 are wrong, or MySQL is stopped |
storage not UP |
The server cannot write to /opt/hdsx/data |
| Nothing after 10 minutes | Read sudo journalctl -u tomcat9 -n 100 |
One error line about the table _security_map not existing, just before the database is built, is
harmless.
3.2 First sign-in
On any computer of the site network, open http://<server address>:8081/hds-explorer-server.
Sign in as admin with Change-me-1. The console shows Change password.
Type Change-me-1 in Current password, then Hdsx-admin-1 twice, and choose Save. The console
opens on the Dashboard. The badge at the top says Site · no code: the site code is set in Using
HDS-Explorer, chapter B.
From now on the administrator signs in with Hdsx-admin-1.
3.3 Remove the first password from the settings
Removes the two administrator settings and restarts Tomcat:
sudo sed -i '/^PROD_ADMIN_/d' /etc/hds-explorer/server.env
sudo cat /etc/hds-explorer/server.env
sudo systemctl restart tomcat9
The file shows four lines, none starting with PROD_ADMIN_. The health address answers UP again
within a minute, and admin / Hdsx-admin-1 still signs in.
3.4 Where you are
| Check | Expected |
|---|---|
curl -s http://localhost:8081/hds-explorer-server/health |
"status":"UP", "version":"1.0.0 build 7" |
Sign in as admin / Hdsx-admin-1 |
The Dashboard |
/etc/hds-explorer/server.env |
Four lines, no PROD_ADMIN_ |
Next: set up the site in Using HDS-Explorer, chapter B.
4. Running the server
Start and stop the server, and read its logs.
4.1 What is running
Two services, both started with the machine: mysql (the database) and tomcat9 (the server and the
console). Starting Tomcat starts MySQL first.
4.2 Stop and start
# stop (MySQL last)
sudo systemctl stop tomcat9
sudo systemctl stop mysql
# start (Tomcat also starts MySQL)
sudo systemctl start tomcat9
# after a settings change: Tomcat only
sudo systemctl restart tomcat9
# state
systemctl is-active mysql tomcat9
Tomcat stops in a second or two and starts in under a minute. Until the health address says UP, the
console shows an error page and the tablets cannot synchronise:
curl -s http://localhost:8081/hds-explorer-server/health; echo
Stopping Tomcat cuts off the console's users and any tablet that is synchronising. A cut-off tablet sends again at its next Sync; nothing is lost. Stop outside working hours and tell the console's users first.
4.3 After the machine restarts
Both services start by themselves. After a few minutes, systemctl is-active mysql tomcat9 says
active twice and the health address says "status":"UP". If it does not answer after 10 minutes, read
sudo journalctl -u tomcat9 -b.
4.4 The logs
| Log | Where | What it holds |
|---|---|---|
| The server's errors | /opt/hdsx/data/logs/server.log |
Every error since it started, and each change on Settings › Permissions. Read this first |
| Tomcat and the server's messages | sudo journalctl -u tomcat9 |
Starting, the database being built, tablet files written |
| Tomcat's own messages | /opt/tomcat9/logs/catalina.<date>.log |
Starting and stopping |
| MySQL | /var/log/mysql/error.log |
MySQL's errors |
An empty server.log is good news: nothing has gone wrong since the server started.
Reads a log:
# the last 50 lines
sudo tail -n 50 /opt/hdsx/data/logs/server.log
# follow it while something happens (Ctrl+C to stop)
sudo tail -f /opt/hdsx/data/logs/server.log
# today's errors in Tomcat's journal
sudo journalctl -u tomcat9 --since today | grep -E 'SEVERE|ERROR'
server.log cleans itself up after 30 days. Tomcat's files and the tablet-file logs
(generate-* in /opt/hdsx/data/logs) never do. Lists those older than 90 days; add -delete to
remove them:
sudo find /opt/tomcat9/logs -type f -mtime +90 -print
sudo find /opt/hdsx/data/logs -name 'generate-*' -mtime +90 -print
What to send when asking for help
server.log, and theserver-<date>.<n>.log.gzfiles of the days the problem happened.- Tomcat's journal of the same days:
sudo journalctl -u tomcat9 --since <first day> --until <day after the last> > tomcat9.log(days as2026-10-29). - What was done, on which page or tablet, and at what time.
5. Backup and upgrade
A nightly copy of the database, how to bring it back, and how to move to a new version.
The database hds_explorer_db holds everything the site records. A backup is a copy of it as one
.sql file. The data folder, /opt/hdsx/data, needs no backup: Execute all writes the tablet
files again.
5.1 A nightly backup
Creates the backup's sign-in file, readable only by root:
sudo mkdir -p /opt/hdsx/backups
sudo chmod 700 /opt/hdsx/backups
sudo tee /opt/hdsx/backups/backup.cnf > /dev/null <<'EOF'
[client]
host=127.0.0.1
port=3307
user=hdsx_app
password=Hdsx-app-1
EOF
sudo chmod 600 /opt/hdsx/backups/backup.cnf
Creates the script. It writes one file per run and deletes backups older than 14 days:
sudo tee /opt/hdsx/backups/backup.sh > /dev/null <<'EOF'
#!/bin/sh
set -e
dir=/opt/hdsx/backups
file="$dir/hds_explorer_db-$(date +%Y-%m-%d_%H%M).sql"
mysqldump --defaults-extra-file="$dir/backup.cnf" --single-transaction --routines --triggers --no-tablespaces --default-character-set=utf8mb4 --result-file="$file" hds_explorer_db
find "$dir" -name 'hds_explorer_db-*.sql' -mtime +14 -delete
EOF
sudo chmod 700 /opt/hdsx/backups/backup.sh
Runs it every night at 22:00 (choose a time when field workers do not synchronise):
sudo tee /etc/systemd/system/hds-explorer-backup.service > /dev/null <<'EOF'
[Unit]
Description=HDS-Explorer database backup
Requires=mysql.service
After=mysql.service
[Service]
Type=oneshot
ExecStart=/opt/hdsx/backups/backup.sh
EOF
sudo tee /etc/systemd/system/hds-explorer-backup.timer > /dev/null <<'EOF'
[Unit]
Description=HDS-Explorer database backup, every night
[Timer]
OnCalendar=*-*-* 22:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now hds-explorer-backup.timer
systemctl list-timers hds-explorer-backup.timer
The list shows the timer with its next run at 22:00.
Runs one backup now and checks it:
sudo systemctl start hds-explorer-backup.service
systemctl status hds-explorer-backup.service --no-pager | grep -E 'Active|status='
sudo ls -l /opt/hdsx/backups
sudo sh -c 'head -1 /opt/hdsx/backups/hds_explorer_db-*.sql'
The status says status=0/SUCCESS, there is a .sql file, and its first line starts with
-- MySQL dump.
At least once a week, copy the newest file to another machine or an external disk kept away from the server.
5.2 Bring a backup back
This replaces the whole database: everything recorded after the backup was made is lost.
-
Stops the server and empties the database (fill in the backup's file name):
file=/opt/hdsx/backups/hds_explorer_db-<date>_<time>.sql sudo systemctl stop tomcat9 mysql -h 127.0.0.1 -P 3307 -u root -pHdsx-root-1 -e "DROP DATABASE hds_explorer_db; CREATE DATABASE hds_explorer_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" -
Loads the backup. It prints nothing when it works:
sudo sh -c "mysql --defaults-extra-file=/opt/hdsx/backups/backup.cnf --default-character-set=utf8mb4 hds_explorer_db < $file" -
Starts the server. Wait for the health address to say
UP(chapter 3.1):sudo systemctl start tomcat9 -
Sign in to the console and run Tablets › Tablet export › Execute all, so the tablets download what the database now holds.
5.3 Upgrade to a new version
The upgrade keeps the database and the settings and replaces the server's folder. Allow 15 minutes,
outside the hours field workers synchronise. server.json names the new version.
-
Download the new server zip (and the release pack, if it is new) with the commands of chapter 2.6. Each file prints
OK. -
Back up the database:
sudo systemctl start hds-explorer-backup.service sudo ls -lt /opt/hdsx/backups | head -3The newest file has the time of now.
-
Stop the server and set the old version aside:
sudo systemctl stop tomcat9 sudo mkdir -p /opt/hdsx/previous sudo rm -rf /opt/hdsx/previous/hds-explorer-server sudo mv /opt/tomcat9/webapps/hds-explorer-server /opt/hdsx/previous/ sudo rm -rf /opt/tomcat9/work/Catalina/localhost/hds-explorer-server -
Put the new version in (fill in the new zip's name) and start it:
sudo unzip -q /opt/hdsx/downloads/hds-explorer-server-<version>-build<n>.zip -d /opt/tomcat9/webapps sudo chown -R hdsx:hdsx /opt/tomcat9/webapps/hds-explorer-server sudo systemctl start tomcat9 -
Wait for the health address (chapter 3.1). It says
UPand the new version:{"status":"UP", … ,"version":"1.0.0 build <n>"} -
Import the new release pack, if there is one, as in Using HDS-Explorer, chapter F (Forms).
-
Write the tablet files: Tablets › Tablet export › Execute all.
Keep the old version in /opt/hdsx/previous until the new one has worked for a few days.
5.4 Go back to the previous version
If the new version does not start or does not work:
-
Stops the server and removes the new version:
sudo systemctl stop tomcat9 sudo rm -rf /opt/tomcat9/webapps/hds-explorer-server /opt/tomcat9/work/Catalina/localhost/hds-explorer-server -
Bring back the backup made before the upgrade: chapter 5.2, steps 1 and 2.
-
Puts the old version back and starts it:
sudo mv /opt/hdsx/previous/hds-explorer-server /opt/tomcat9/webapps/ sudo systemctl start tomcat9 -
The health address says
UPand the old version. Run Execute all.