Eclipse RDF4J
Overview
Eclipse RDF4J is a Java framework and server for RDF and linked data. It stores RDF triples and answers SPARQL 1.1 queries and updates over HTTP.
This SecureAnyCloud image installs RDF4J 5.3.2 with both official web applications deployed into Apache Tomcat 9. RDF4J Server is the triplestore and its SPARQL endpoint. RDF4J Workbench is the browser-based admin UI for creating repositories, loading data and running queries.
Read this before you expose the VM. Upstream RDF4J ships with no authentication of any kind: its web.xml carries the security constraint commented out, and its SPARQL endpoint accepts UPDATE and DELETE. On a stock deployment, anything that can reach the port can create or wipe every repository. This image closes that gap. Tomcat is bound to 127.0.0.1 only, and nginx is the sole public listener, enforcing HTTP Basic authentication with a password generated uniquely on the first boot of every VM. Every request, whether from a browser, curl or a SPARQL client, must authenticate.
What you get:
- Eclipse RDF4J 5.3.2 (BSD-3-Clause / Eclipse Distribution License v1.0) on OpenJDK 17
- Apache Tomcat 9.0.120, bound to
127.0.0.1:8080only, with its shutdown port disabled - RDF4J Server at
/rdf4j-server: the triplestore plus the SPARQL 1.1 query and update endpoint - RDF4J Workbench at
/rdf4j-workbench: the browser-based admin UI - nginx on port 80 as the only public listener, enforcing HTTP Basic authentication for every path
- A per-VM password generated on first boot and recorded in a root-only file
- File-backed native-store repositories under
/var/lib/rdf4j - An empty store on first boot: you create the first repository, and nothing is pre-seeded
- No Tomcat accounts and no Tomcat manager, host-manager, examples or docs webapps
- An unauthenticated
/healthendpoint for Azure Load Balancer health probes - 24/7 SecureAnyCloud support
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2ms (2 vCPU / 8 GiB RAM) is a reasonable starting size. Choose a larger size for bigger datasets.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp for the Workbench and the SPARQL endpoint. The image serves plain HTTP on port 80, so for production, terminate TLS in front of it with your own domain.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Eclipse RDF4J by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).
Select Review + create, then Create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:rdf4j-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name rdf4j \
--image sac1647283583153:rdf4j-ubuntu-24-04:default:latest \
--size Standard_B2ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard \
--os-disk-delete-option Delete --nic-delete-option DeleteStep 3 - Retrieve the per-VM password
Every VM generates its own password on first boot. Nothing is shared between deployments, and there is no default login. SSH in and read the credentials file:
sudo cat /root/rdf4j-credentials.txtThe file is mode 0600 and owned by root. It records the Workbench and server URLs, the username admin, and RDF4J_ADMIN_PASSWORD.
First boot normally completes within a minute of the VM reaching Running. If the file does not yet show the generated password, wait for rdf4j-firstboot.service to finish:
sudo systemctl is-active rdf4j-firstboot.serviceStep 4 - Verify the deployment
Confirm that the services are up, the health probe answers, and RDF4J is reachable only through the authenticated proxy:
sudo systemctl is-active tomcat.service nginx.service rdf4j-firstboot.service curl -s -o /dev/null -w 'health (no auth): HTTP %{http_code}\n' http://127.0.0.1/health curl -s -o /dev/null -w 'workbench (no auth): HTTP %{http_code}\n' http://127.0.0.1/rdf4j-workbench/The health probe returns 200 without credentials, and the Workbench returns 401. This is the intended behavior.
Tomcat listens on loopback only. Confirm that nothing but nginx is exposed:
ss -tlnH | awk '{print $4}' | grep -E ':(80|8080)$' | sort -unginx listens publicly on port 80, and Tomcat on 8080 is bound to [::ffff:127.0.0.1], the IPv6-mapped form of 127.0.0.1 (loopback). RDF4J is therefore reachable from the network only through the authenticated nginx listener on port 80.
Step 5 - Sign in to the Workbench
Open http://<your-vm-public-ip>/ in a browser. You are redirected to the Workbench and prompted for HTTP Basic credentials. Enter username admin and the password from Step 3. The store is empty on a new VM, so the repository list starts with no entries.
The header shows RDF4J Server: http://127.0.0.1:8080/rdf4j-server. This is correct and deliberate: the Workbench reaches the server directly over loopback, behind the nginx authentication layer, rather than looping back through the public port.
Step 6 - Create your first repository
In the Workbench, choose New repository, pick a type, and give it an ID and title. Native Store keeps data on disk and is the right choice for most deployments. The Type list opens on Memory Store, which is not persisted across a restart unless you enable persistence.
You can do the same over the REST API. Run the following to create a native-store repository called example:
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-)
cat > /tmp/example-repo.ttl <<'TTL' @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix config: <tag:rdf4j.org,2023:config/>. [] a config:Repository ; config:rep.id "example" ; rdfs:label "Example repository" ; config:rep.impl [ config:rep.type "openrdf:SailRepository" ; config:sail.impl [ config:sail.type "openrdf:NativeStore" ; config:native.tripleIndexes "spoc,posc" ] ]. TTL curl -s -o /dev/null -w 'create repository: HTTP %{http_code}\n' -u "admin:$PW" \
-X PUT -H 'Content-Type: text/turtle' --data-binary @/tmp/example-repo.ttl \
http://127.0.0.1/rdf4j-server/repositories/exampleAn HTTP 204 means the repository was created. List the repositories to confirm:
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" -H 'Accept: text/csv' http://127.0.0.1/rdf4j-server/repositoriesStep 7 - Load RDF data
Post your data to the repository's statements endpoint. Turtle, N-Triples, RDF/XML and JSON-LD are all accepted:
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-)
cat > /tmp/example-data.ttl <<'TTL' @prefix dc: <http://purl.org/dc/elements/1.1/> . @prefix ex: <http://example.org/library/> . ex:weaving dc:title "Weaving the Web" ; dc:creator "Tim Berners-Lee" ; dc:date "1999" . ex:primer dc:title "A Semantic Web Primer" ; dc:creator "Grigoris Antoniou" ; dc:date "2004" . ex:linkeddata dc:title "Linked Data" ; dc:creator "David Wood" ; dc:date "2014" . TTL curl -s -o /dev/null -w 'load triples: HTTP %{http_code}\n' -u "admin:$PW" \
-X POST -H 'Content-Type: text/turtle' --data-binary @/tmp/example-data.ttl \
http://127.0.0.1/rdf4j-server/repositories/example/statements printf 'triples now stored: ' curl -s -u "admin:$PW" http://127.0.0.1/rdf4j-server/repositories/example/size echoStep 8 - Run a SPARQL query
Send a query over HTTP and request CSV results:
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" -H 'Accept: text/csv' -G \
http://127.0.0.1/rdf4j-server/repositories/example \
--data-urlencode 'query=PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?title ?creator ?year WHERE { ?b dc:title ?title ; dc:creator ?creator ; dc:date ?year } ORDER BY ?year'Send Accept: application/sparql-results+json to receive JSON instead. The same query on the Workbench Query page renders as a table.
The Workbench Summary page reports the repository ID, its location and the number of stored statements.
Step 9 - Run a SPARQL update
Send INSERT DATA, DELETE WHERE and the rest of SPARQL 1.1 Update to the same statements endpoint as an update parameter:
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'insert: HTTP %{http_code}\n' -u "admin:$PW" \
-X POST --data-urlencode \
'update=PREFIX dc: <http://purl.org/dc/elements/1.1/> INSERT DATA { <http://example.org/library/rdf4j> dc:title "Learning SPARQL" ; dc:creator "Bob DuCharme" ; dc:date "2013" }' \
http://127.0.0.1/rdf4j-server/repositories/example/statements printf 'triples after insert: '
curl -s -u "admin:$PW" http://127.0.0.1/rdf4j-server/repositories/example/size echoStep 10 - Confirm the authentication layer
This is the most important check on the image, because upstream RDF4J has no access control of its own. Every destructive operation must be refused without the per-VM credential:
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-) C() { curl -s -o /dev/null -w '%{http_code}' -m 15 "$@" 2>/dev/null; } S=http://127.0.0.1/rdf4j-server printf 'anon GET repository list: HTTP %s\n' "$(C $S/repositories)" printf 'anon POST SPARQL UPDATE DROP ALL: HTTP %s\n' "$(C -X POST --data-urlencode 'update=DROP ALL' $S/repositories/example/statements)" printf 'anon DELETE every triple: HTTP %s\n' "$(C -X DELETE $S/repositories/example/statements)" printf 'anon DELETE the repository: HTTP %s\n' "$(C -X DELETE $S/repositories/example)" printf 'wrong password: HTTP %s\n' "$(C -u admin:definitely-wrong $S/repositories)" printf 'per-VM credential: HTTP %s\n' "$(C -u "admin:$PW" $S/repositories)"Every anonymous and wrong-password attempt returns 401, and only the per-VM credential returns 200.
With the per-VM credential, the full round trip works: list repositories, count triples and run a SPARQL SELECT.
Step 11 - Change the password
The Basic-auth credential lives in an nginx htpasswd file. Run the following to rotate the password or add a second user:
sudo htpasswd -b /etc/nginx/.rdf4j_htpasswd admin '<new-password>'
sudo systemctl reload nginxAfterwards, update /root/rdf4j-credentials.txt so the recorded password stays accurate.
Maintenance
Service management. Tomcat hosts both web applications; nginx is the public listener.
sudo systemctl status tomcat.service --no-pager | head -12Logs. Tomcat writes to /opt/tomcat/logs, and the units log to the journal:
sudo journalctl -u tomcat.service --no-pager | tail -20Where the data lives. Repositories are stored under /var/lib/rdf4j/server/repositories, one directory per repository. Back up that path, use the Workbench Export page, or GET the statements endpoint to dump a repository as RDF.
PW=$(sudo grep '^RDF4J_ADMIN_PASSWORD=' /root/rdf4j-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" -H 'Accept: text/turtle' \
http://127.0.0.1/rdf4j-server/repositories/example/statements | head -12JVM heap. The image caps Tomcat's heap explicitly at 1 GiB, which suits the default Standard_B2ms. For larger datasets, raise -Xmx in CATALINA_OPTS and restart Tomcat:
sudo systemctl edit tomcat.service
# add, adjusting to taste and leaving headroom for the OS:
# [Service]
# Environment="CATALINA_OPTS=-Xms512m -Xmx<your-heap-size> -XX:MaxMetaspaceSize=256m \
# -Dorg.eclipse.rdf4j.appdata.basedir=/var/lib/rdf4j -Djava.awt.headless=true" sudo systemctl daemon-reload && sudo systemctl restart tomcat.serviceTLS. The image serves plain HTTP. Put TLS in front of it before you expose the VM to an untrusted network. Use either an Azure Application Gateway or Front Door to terminate TLS, or certbot on the VM itself with your own domain:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>Updates. Unattended security upgrades are enabled. Apply OS updates and reboot on your own schedule:
sudo apt-get update && sudo apt-get -y dist-upgradeSupport
24/7 support is included with this image. Contact sales@secureanycloud.com with your Azure subscription ID and the VM name.
Deploy on Azure
Launch Eclipse RDF4J on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com