BaseX 12.4
Overview
This guide covers launching and using BaseX 12.4 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Apache Cassandra, FerretDB & NoSQL Database AMIs listing on AWS Marketplace. You select it as the software version when you subscribe and launch. The other databases in the listing are described on the listing's guide hub.
BaseX is a light, fast native XML database and XQuery 3.1 processor. It stores XML (and JSON, CSV and binary resources) in indexed databases, answers XPath, XQuery and full-text queries, updates documents with the XQuery Update Facility and serves results over HTTP. You work with it through the DBA web admin (databases, an XQuery editor, users, jobs and logs), the REST API, RESTXQ web applications that you write in XQuery, and the basexclient command line and language bindings that talk to the BaseX database server.
BaseX's admin user is a superuser: XQuery can read and write files, fetch URLs and run programs. A stock install starts with a random admin password written in clear text to its log, and the HTTP server listens on every interface. The SecureAnyCloud image changes both. What you get:
- An admin password unique to your instance. First boot sets the BaseX
adminpassword with BaseX's ownALTER PASSWORDcommand. The password is read from a private temporary file, never from a command line or a log, and is written to the root-only file/root/basex-credentials.txt. BaseX stores only salted hashes. The image ships no user file, BaseX refuses to start until first boot has set the password, and the defaultadmin/adminlogin is refused on the DBA, the REST API and the database server. - HTTPS from the first boot. nginx is the only network entry point. It publishes the DBA and the REST API on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS. Nothing else that BaseX serves is published (the root URL opens the DBA).
- BaseX on localhost only. The BaseX HTTP server (8984) and the BaseX database server (1984) listen on
127.0.0.1only. Never open those ports in a security group. - XQuery confined. BaseX runs as its own
basexuser with no capabilities and a read-only system. XQuery can write only the data volume, and it cannot see home directories, the TLS key, nginx, the system logs or/etc/shadow. Neither BaseX nor nginx can reach the EC2 instance metadata endpoint, so an XQueryhttp:send-requestorfetch:textcannot read instance credentials. - Fixed host name and real client addresses. Requests for a host name that the instance does not serve are dropped. nginx logs the real client address (client-supplied
X-Forwarded-Forheaders are overwritten), the DBA session cookie is HTTPS-only, and cross-site browser requests that change state are refused. DBA logins are rate-limited. - Your data on its own disk. Databases, the user file, the BaseX logs and the XQuery package repository live on a dedicated 20 GiB EBS volume mounted at
/var/lib/basex. You can resize it independently of the root volume. - Verified download. The BaseX 12.4 release zip was checked against a pinned SHA-256. Its
BaseX.jaris byte-identical to theorg.basex:basex:12.4artifact whose SHA-256 BaseX publishes on Maven Central. - Key-only SSH. Password logins, root logins and X11 forwarding are switched off.
- Patched base. Ubuntu 24.04 LTS, fully patched at build time, with unattended security upgrades enabled.
What is on the image: BaseX 12.4 (/opt/basex, BSD-3-Clause), Ubuntu's OpenJDK 21 runtime, nginx, Ubuntu 24.04 LTS, 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) from your own networks and TCP 443 (DBA and REST) from the networks and applications that use BaseX. TCP 80 only redirects to HTTPS and is optional. Do not open 1984 or 8984.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick BaseX 12.4 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2.
Choose m5.large, your key pair and a security group as described above. The AMI brings its own 20 GiB data volume for BaseX. Increase its size on the launch page if you expect more data (Step 9).
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --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=basex}]'Connect to your instance
Version: BaseX 12.4 on Ubuntu 24.04.
SSH user: ubuntu.
ssh -i <your-key.pem> ubuntu@<public-ip>Check the services
First boot generates the certificate and the admin password, then starts BaseX and nginx. BaseX is ready a few seconds after boot:
systemctl is-active basex nginx basex-firstboot
sudo test -f /var/lib/sac/data/basex-firstboot.done && echo FIRSTBOOT_DONE java -cp /opt/basex/BaseX.jar org.basex.BaseX -h 2>&1 | head -1 java -version 2>&1 | head -1active active active FIRSTBOOT_DONE BaseX 12.4 [Standalone] openjdk version "21.0.12.1" 2026-08-18Only SSH and nginx listen on public addresses. The BaseX HTTP server (8984) and database server (1984) listen on loopback:
sudo ss -Hlntp | awk '{print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:1984 users:(("java")) 127.0.0.1:8984 users:(("java")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))Get your URLs and password
The URLs and the user name are in the root-only credentials file:
sudo grep -E '^BASEX_(DBA_URL|REST_URL|ADMIN_USER)=' /root/basex-credentials.txtBASEX_DBA_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/dba BASEX_REST_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/rest BASEX_ADMIN_USER=adminThe password is the BASEX_ADMIN_PASSWORD line of the same file: sudo grep '^BASEX_ADMIN_PASSWORD=' /root/basex-credentials.txt. Store it in your password manager. The password is unique to this instance, and admin is the BaseX superuser.
The URLs use the instance's public DNS name from EC2 instance metadata (IMDSv2), or its public or private IPv4 address when it has no public name. The certificate names that host plus the instance's public and private IP addresses:
openssl x509 -in /etc/ssl/basex/basex.crt -noout -subject -ext subjectAltName -enddatesubject=CN = ec2-<public-ip-dashed>.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost notAfter=<date about 27 months after first boot>Without the password, the REST API answers 401, the default admin/admin login is refused, the DBA sends you to its login page, and port 80 redirects to HTTPS.
The following commands read the password from the credentials file into a shell variable and pass it to curl through a file descriptor. The password never appears in the process list or your shell history:
H=$(sudo sed -n 's#^BASEX_DBA_URL=https://\([^/]*\)/.*#\1#p' /root/basex-credentials.txt)
PW=$(sudo sed -n 's/^BASEX_ADMIN_PASSWORD=//p' /root/basex-credentials.txt) R=(--cacert /etc/ssl/basex/basex.crt --resolve "$H:443:127.0.0.1") curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/dba" curl -s -o /dev/null -w 'REST, no login -> HTTP %{http_code}\n' "${R[@]}" "https://$H/rest" curl -s -o /dev/null -w 'REST, admin:admin -> HTTP %{http_code}\n' "${R[@]}" -K <(printf 'user = "admin:admin"\n') "https://$H/rest" curl -s -o /dev/null -w 'REST, your password -> HTTP %{http_code}\n' "${R[@]}" -K <(printf 'user = "admin:%s"\n' "$PW") "https://$H/rest" curl -s -o /dev/null -w 'DBA, no login -> %{http_code} %{redirect_url}\n' "${R[@]}" "https://$H/dba/databases" curl -s -w ' <- /healthz (no login) HTTP %{http_code}\n' "${R[@]}" "https://$H/healthz"http:// -> 301 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/dba REST, no login -> HTTP 401 REST, admin:admin -> HTTP 401 REST, your password -> HTTP 200 DBA, no login -> 302 https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/dba/login?_page=databases ok <- /healthz (no login) HTTP 200--resolve sends the request to this instance while keeping the real host name, so the certificate check passes. From another machine, omit --resolve and use the URL directly.
Create a database and query it over REST
The REST API takes the same user and password with HTTP Basic authentication over HTTPS. This example creates a library database from an XML document with PUT, runs two XQuery expressions against it, lists the databases and returns a JSON summary. The small bx function reads the password as before and hands it to curl through a file descriptor:
H=$(sudo sed -n 's#^BASEX_REST_URL=https://\([^/]*\)/.*#\1#p' /root/basex-credentials.txt)
PW=$(sudo sed -n 's/^BASEX_ADMIN_PASSWORD=//p' /root/basex-credentials.txt) bx() { curl -s --cacert /etc/ssl/basex/basex.crt --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") "$@"; echo; } bx -X PUT -H 'Content-Type: application/xml' --data-binary '<library> <book id="b1"><title>XQuery</title><author>Priscilla Walmsley</author><year>2015</year></book> <book id="b2"><title>XML in a Nutshell</title><author>Elliotte Rusty Harold</author><year>2004</year></book> <book id="b3"><title>Designing Data-Intensive Applications</title><author>Martin Kleppmann</author><year>2017</year></book> </library>' "https://$H/rest/library" bx --get --data-urlencode 'query=for $b in //book order by xs:integer($b/year) descending return $b/title/string()' "https://$H/rest/library" bx --get --data-urlencode 'query=//book[xs:integer(year) gt 2010]/author/string()' "https://$H/rest/library" bx "https://$H/rest" bx --get --data-urlencode 'query=json:serialize(map { "books": count(//book), "newest": max(//book/year ! xs:integer(.)) })' "https://$H/rest/library"Database 'library' created in 7.06 ms. Designing Data-Intensive Applications XQuery XML in a Nutshell Priscilla Walmsley Martin Kleppmann <databases xmlns="http://basex.org/rest"><database resources="1" size="5533">library</database></databases> {"books":3,"newest":2017}GET /rest/<database> without a query returns the stored documents, DELETE /rest/<database> drops a database, and POST takes a query or command document. See the BaseX documentation on the REST API for the full set of operations.
Use the DBA web admin
Open the BASEX_DBA_URL from Step 4 (https://<your-host>/dba) in a browser. The certificate is self-signed, so the browser warns once. Accept the warning, or install your own certificate (Step 10). Sign in as admin with the password from the credentials file.
Databases lists every database with its resource count, size and last change. You can create, optimize, drop, back up and restore databases there. Select a database to see its resources, its backups, and its properties and indexes.
Editor runs XQuery against your databases and shows the result next to the query. Press Ctrl-Enter to run a query.
Users creates further users and sets their permissions (read, write, create or admin). Logs shows every request with its user, address and timing. Give applications their own user with the least permission they need, rather than the admin superuser.
Use basexclient on the instance
The BaseX command line client is on every login's PATH. It connects to the BaseX database server on 127.0.0.1:1984 and asks for the password. When standard input is not a terminal, it reads the password from there, so you can pipe it straight from the credentials file:
sudo sed -n 's/^BASEX_ADMIN_PASSWORD=//p' /root/basex-credentials.txt | /opt/basex/bin/basexclient -U admin -c 'XQUERY count(db:get("library")//book)'; echoPassword: 3Run basexclient -U admin without -c for an interactive session. The client prompts for the password. Client libraries for Java, Python, Node.js and other languages speak the same protocol. Run them on the instance, or forward the port over SSH from your workstation and connect to localhost:1984:
ssh -i <your-key.pem> -N -L 1984:127.0.0.1:1984 ubuntu@<public-ip>Never open 1984 or 8984 in a security group. For applications on other hosts, use the REST API over HTTPS (Step 5).
XQuery confinement and instance metadata
XQuery functions run inside the BaseX service. proc:system runs programs as the unprivileged basex user. The file functions see a read-only system with the secrets hidden, and requests to the instance metadata address are dropped:
H=$(sudo sed -n 's#^BASEX_REST_URL=https://\([^/]*\)/.*#\1#p' /root/basex-credentials.txt)
PW=$(sudo sed -n 's/^BASEX_ADMIN_PASSWORD=//p' /root/basex-credentials.txt) xq() { curl -s --cacert /etc/ssl/basex/basex.crt --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") --get --data-urlencode "query=$1" "https://$H/rest"; echo; } xq 'normalize-space(proc:system("id", "-un"))' xq 'try { file:read-text("/etc/shadow") } catch * { "refused: " || $err:description }' xq 'try { file:write-text("/var/lib/sac/config/sac-test", "x") } catch * { "refused: " || $err:description }' xq 'try { http:send-request(<http:request method="put" timeout="5"><http:header name="X-aws-ec2-metadata-token-ttl-seconds" value="60"/></http:request>, "http://169.254.169.254/latest/api/token")[1]/@status/string() } catch * { "refused: " || $err:description }'basex refused: Access to '/etc/shadow' is denied. refused: Resource "/var/lib/sac/config/sac-test (Read-only file system)" not found. refused: HTTP connect timed outThe following commands show the service settings behind this behavior:
systemctl show basex -p User -p NoNewPrivileges -p ProtectSystem -p ProtectHome -p PrivateTmp -p IPAddressDeny
systemctl show nginx -p IPAddressDenyIPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32 User=basex PrivateTmp=yes ProtectHome=yes ProtectSystem=strict NoNewPrivileges=yes IPAddressDeny=fd00:ec2::254/128 169.254.169.254/32 168.63.129.16/32Storage, memory and logs
BaseX keeps its databases, the user file users.xml and its logs under /var/lib/basex/data, and the XQuery package repository under /var/lib/basex/repo, on the dedicated EBS volume. The JVM runs with a 2 GB heap:
df -h /var/lib/basex | tail -1 | awk '{print $2" total, "$4" free on "$6}'
sudo grep -E '^(DBPATH|REPOPATH|SERVERHOST|SERVERPORT) ' /opt/basex/.basex grep -oE -- '-Xmx[0-9a-z]+' /usr/local/lib/sac/basex-java20G total, 19G free on /var/lib/basex DBPATH = /var/lib/basex/data REPOPATH = /var/lib/basex/repo SERVERPORT = 1984 SERVERHOST = 127.0.0.1 -Xmx2g- Heap. On a larger instance, raise
-Xmx2gin/usr/local/lib/sac/basex-java(about half of the instance's memory is a good start), then runsudo systemctl restart basex. - Disk. To grow the volume, modify it in the EC2 console (Volumes, Modify volume) and extend the file system online:
sudo resize2fs "$(findmnt -no SOURCE /var/lib/basex)". - Backups. Create database backups in the DBA (Databases, Back up) or with the
CREATE BACKUPcommand. Backups are written next to the databases on the data volume. For a full copy, stop BaseX (sudo systemctl stop basex) and take an EBS snapshot of the data volume. - Logs. BaseX writes one log per day to
/var/lib/basex/data/.logs(also under Logs in the DBA).journalctl -u basexshows the service output, andjournalctl -u basex-firstbootshows what first boot did.
Use your own DNS name or certificate
The served host name is fixed by configuration, never taken from a request. To serve BaseX under your own DNS name, point the name at the instance and run the helper. The helper records the name, regenerates the self-signed certificate for it, updates the credentials file and reloads nginx:
sudo basex-set-hostname <your-dns-name>If the instance's public DNS name changes after a stop and start, run the same command again, because a request for a name that the instance does not serve is dropped. An Elastic IP avoids this.
To use a certificate from your own CA or a public CA instead, replace /etc/ssl/basex/basex.crt (full chain) and /etc/ssl/basex/basex.key (mode 0600, owner root), then run sudo nginx -t && sudo systemctl reload nginx.
Security notes
SSH accepts keys only. Passwords, root logins and X11 forwarding are switched off in /etc/ssh/sshd_config.d/00-sac.conf, which sorts before every other SSH setting. Check the effective values:
sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin|x11forwarding|pubkeyauthentication) 'permitrootlogin no pubkeyauthentication yes passwordauthentication no kbdinteractiveauthentication no x11forwarding noKeep the following points in mind:
- To change the admin password, sign in to the DBA, open Users, then admin, and set a new password (or run
ALTER PASSWORD admininbasexclient). Then update/root/basex-credentials.txt. - The
adminuser can read and write files on the data volume, query other web services and run programs as thebasexuser through XQuery. Use separate users withreadorwritepermission for applications. - nginx logs the real client address of every request, and it ignores
X-Forwarded-Forheaders sent by clients. The AMI requires IMDSv2. Restrict port 443 to the networks that use BaseX. - Bundled libraries. BaseX 12.4 ships Jetty 12.1.8, which a scanner reports as affected by CVE-2026-10050 (high: a DIGEST authentication bypass in Jetty's security module), CVE-2026-6790 (Host header validation), CVE-2026-8384 (a path confusion in Jetty's static file handling) and CVE-2026-10051 (HTTP/1.1 trailers kept across connections). All four are fixed in Jetty 12.1.10. BaseX does not use Jetty's authenticators: it checks the HTTP Basic credentials itself, and this image uses Basic authentication only. Jetty is reached only through nginx, which sends a fixed host name, forwards a normalized path for /dba and /rest only, and re-frames each request. 12.4 is the newest BaseX release, and SecureAnyCloud updates the image when BaseX ships a release with the newer Jetty.
When you have finished with the example database from Step 5, drop it:
H=$(sudo sed -n 's#^BASEX_REST_URL=https://\([^/]*\)/.*#\1#p' /root/basex-credentials.txt)
PW=$(sudo sed -n 's/^BASEX_ADMIN_PASSWORD=//p' /root/basex-credentials.txt)
curl -s --cacert /etc/ssl/basex/basex.crt --resolve "$H:443:127.0.0.1" -K <(printf 'user = "admin:%s"\n' "$PW") -X DELETE "https://$H/rest/library"; echoDatabase 'library' was dropped.Support and licensing
SecureAnyCloud supports this image by email at sales@secureanycloud.com. Include the instance ID, the version (BaseX 12.4 on Ubuntu 24.04) and a description of what you were doing.
BaseX is open source software under the BSD 3-Clause license. The license is on the instance at /opt/basex/LICENSE, and the licenses of the bundled libraries are at /opt/basex/lib/LICENSES. There is no per-user or per-CPU license fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.
BaseX is a trademark of its owner. Ubuntu is a trademark of Canonical Ltd. Java is a trademark of Oracle and/or its affiliates. Eclipse Jetty is a trademark of the Eclipse Foundation. SecureAnyCloud is not affiliated with or endorsed by these organizations.
Deploy on AWS
Launch BaseX 12.4 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