Deployment guide

Apicurio Registry 3.3.3

Overview

This guide covers launching and using the Apicurio Registry 3.3.3 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. The AMI is one version of the SecureAnyCloud streaming and messaging listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.

Apicurio Registry is an open source registry for event schemas and API designs: Avro, Protobuf, JSON Schema, OpenAPI, AsyncAPI, GraphQL, WSDL and XSD. Producers and consumers look schemas up at runtime, and compatibility rules stop a breaking change from being registered.

It has a web UI, its own REST API (v3, plus the older v2) and a Confluent-compatible Schema Registry API. Kafka clients that use Confluent's serializers work with it unchanged, as do Apicurio's own Kafka SerDes.

What you get:

  • HTTPS only, from the first boot. nginx serves the web UI and the APIs on port 443 (port 80 only redirects) with a certificate generated on your instance for its own names and addresses. It is an end-entity certificate, so it can only vouch for this instance and can never sign other certificates. You can install your own CA-issued certificate with one command (Step 12), and the boot-time renewal never replaces it.
  • A login on every page and every API call, enforced twice. Apicurio Registry ships with authentication switched off. On this image, every path asks for a login at the front door (nginx). The registry then authenticates every API call again with its own user store and applies its role-based authorization: admin (everything, including exports and settings), developer (register and change schemas and rules) and reader (read only). Each user's password is generated on your instance at first boot, and failed logins are rate limited. Add, re-key or remove users with sudo apicurio-user (Step 9).
  • No default secret, and a database created on your instance. The image ships no password, key or database. At first boot, the instance creates its own PostgreSQL cluster, the registry's database and its database role. The password is generated on the instance, and PostgreSQL only ever sees its SCRAM-SHA-256 verifier. The registry and nginx do not start until first boot has finished.
  • Everything else on localhost. The registry (8080), its management endpoint with health and metrics (9000) and PostgreSQL (5432) listen on 127.0.0.1 only, checked every time a service starts.
  • Guarded browser requests. The registry refuses requests with another site's Origin, writes that a browser marks as cross-site, WebSocket upgrades and ; path parameters. Exports and downloads of the registry's data must come from the registry's own page. A cross-site request, a link opened from outside the browser or a request without a browser signal gets a 403, so another web page cannot use your browser's saved login to pull your schemas. CORS is off.
  • Instance metadata blocked. The registry and PostgreSQL can reach nothing but localhost, and nginx and the boot service are blocked from the instance metadata service. The AMI requires IMDSv2.
  • Hardened defaults. The registry runs as an unprivileged user under systemd sandboxing. nginx hands it the real client address and the configured host name, never values that a client sends. SSH accepts keys only, and Ubuntu 24.04 LTS is fully patched at build time with unattended security upgrades switched on.

What is on the image: Apicurio Registry 3.3.3 (the official release from Maven Central, verified against its published SHA-512 digest and the release signature), the official Apicurio Registry 3.3.3 web UI (taken from the project's release image by digest), the Eclipse Temurin 21.0.13 Java runtime (verified against its published SHA-256), PostgreSQL 16 and nginx 1.24 (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. The subnet needs a route to the internet, or a private route that you use for management.
  • A security group that allows TCP 22 (SSH) and TCP 443 (the UI and the APIs) from your admin network and from the Kafka clients that use the registry. Also allow TCP 80 if you want plain HTTP requests redirected to HTTPS. You do not need to open anything else. The registry and PostgreSQL listen on the loopback address only and refuse connections from other machines, even if a security group allows them.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe and then Continue to Configuration. Pick Apicurio Registry 3.3.3 on Ubuntu 24.04 as the software version, pick your Region, and choose Launch through EC2. Choose m5.large, your key pair and a security group as described above.

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=apicurio-registry}]'

First boot takes about a minute after the instance is running. The boot service creates the PostgreSQL cluster, the users and the certificate. Then PostgreSQL, the registry (which creates its tables) and nginx start.

2

Connect to your instance

Version SSH user

Apicurio Registry 3.3.3 on Ubuntu 24.04 ubuntu

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services and the listening set

systemctl is-active apicurio-metadata-names apicurio-firstboot postgresql@16-main apicurio-registry nginx
sudo test -f /var/lib/sac/data/apicurio-firstboot.done && echo FIRSTBOOT_DONE

All five units are active, and the first-boot marker is present:

active active active active active FIRSTBOOT_DONE

Only SSH and nginx listen on network addresses. PostgreSQL (5432), the registry (8080) and its management endpoint (9000) listen on 127.0.0.1:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.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:5432 users:(("postgres")) 127.0.0.1:8080 users:(("java")) 127.0.0.1:9000 users:(("java")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))
4

Get the URLs, the logins and the certificate fingerprint

Only root can read the credentials file. It holds the URLs, the three logins with their roles, the SHA-256 fingerprint of this instance's certificate and the registry's own database login, which only the registry uses:

sudo cat /root/apicurio-credentials.txt
# Apicurio Registry 3.3.3 on Ubuntu 24.04 - generated on first boot by apicurio-firstboot.service.
# Every password is unique to this instance; none was baked into the image. Keep this file secret.
# The registry and its UI are served over HTTPS only (port 443; port 80 redirects). The certificate is self signed
# (minted on this instance): compare its SHA-256 fingerprint with APICURIO_TLS_CERT_SHA256 below before you sign in,
# or install a CA-issued certificate with:
sudo apicurio-install-cert <chain.pem> <key.pem>
# Users and roles: admin = sr-admin, developer = sr-developer (register and change artifacts), reader = sr-readonly.
# Manage users:
sudo apicurio-user list|add|passwd|remove Re-verify the appliance:
sudo apicurio-selfcheck APICURIO_URL=https://<public-ip>/ APICURIO_HOST=<public-ip> APICURIO_API_URL=https://<public-ip>/apis/registry/v3 APICURIO_CCOMPAT_URL=https://<public-ip>/apis/ccompat/v7 APICURIO_TLS_CERT_SHA256=<sha256-fingerprint> APICURIO_TLS_CERT_FILE=/etc/sac/apicurio-registry.pem APICURIO_ADMIN_USER=admin APICURIO_ADMIN_ROLE=sr-admin APICURIO_ADMIN_PASSWORD=<APICURIO_ADMIN_PASSWORD> APICURIO_DEVELOPER_USER=developer APICURIO_DEVELOPER_ROLE=sr-developer APICURIO_DEVELOPER_PASSWORD=<APICURIO_DEVELOPER_PASSWORD> APICURIO_READER_USER=reader APICURIO_READER_ROLE=sr-readonly APICURIO_READER_PASSWORD=<APICURIO_READER_PASSWORD>
# PostgreSQL (127.0.0.1:5432; the registry's own login, used by the registry only): APICURIO_DB_NAME=apicurio APICURIO_DB_USER=apicurio APICURIO_DB_PASSWORD=<generated> # REST API with a login from a curl config (never on the command line): # sudo curl --cacert /etc/sac/apicurio-registry.pem -K /root/apicurio-admin.curl https://<public-ip>/apis/registry/v3/system/info # User guide: https://secureanycloud.com/stacks/apicurio-registry-3-3-3/

The URLs, the users and the fingerprint (without the passwords):

sudo grep -E '^APICURIO_(URL|API_URL|CCOMPAT_URL|TLS_CERT_SHA256|[A-Z]+_USER|[A-Z]+_ROLE)=' /root/apicurio-credentials.txt
APICURIO_URL=https://<public-ip>/ APICURIO_API_URL=https://<public-ip>/apis/registry/v3 APICURIO_CCOMPAT_URL=https://<public-ip>/apis/ccompat/v7 APICURIO_TLS_CERT_SHA256=<sha256-fingerprint> APICURIO_ADMIN_USER=admin APICURIO_ADMIN_ROLE=sr-admin APICURIO_DEVELOPER_USER=developer APICURIO_DEVELOPER_ROLE=sr-developer APICURIO_READER_USER=reader APICURIO_READER_ROLE=sr-readonly APICURIO_DB_USER=apicurio

The host is the instance's public IP address from the EC2 instance metadata, or the private address when the instance has no public one. The certificate also names the public DNS name, the private IP address and the private DNS name. If you stop and start the instance without an Elastic IP, the boot service picks up the new address and issues a new certificate for it, unless you installed your own (Step 12).

To read the certificate's fingerprint and names from the instance, run:

sudo openssl x509 -in /etc/sac/apicurio-registry.pem -noout -fingerprint -sha256 openssl x509 -in /etc/sac/apicurio-registry.pem -noout -ext subjectAltName,basicConstraints,keyUsage,extendedKeyUsage
sha256 Fingerprint=<sha256-fingerprint> X509v3 Subject Alternative Name: IP Address:<public-ip>, DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<private-ip>, DNS:ip-<private-ip-dashed>.ec2.internal, IP Address:127.0.0.1, DNS:localhost X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication

Calling the API from the instance itself. The examples below run on the instance as root. They read the login from a curl config file (-K /root/apicurio-<user>.curl), so no password is ever on a command line, and they pin this instance's certificate (--cacert). They send the request to the registry's own name but connect to 127.0.0.1 (--resolve), because a connection from the instance to its own public address leaves through the internet gateway and is then subject to your security group. From your own machine, use the URL directly.

5

Open the web UI

Browse to the APICURIO_URL from Step 4. The certificate is self-signed and unique to this instance, so the browser warns about it the first time. Check the certificate before you continue. Open the browser's certificate viewer for the page and compare its SHA-256 fingerprint with APICURIO_TLS_CERT_SHA256 from the credentials file. Continue only if they match, and only for this site.

Never add this certificate to an operating-system or browser trust store. For a certificate that browsers trust without a warning, install a CA-issued one (Step 12).

The browser then asks for a user name and password. Sign in as admin, developer or reader with the matching password from the credentials file. To print a single password on the instance, run:

sudo awk -F= '$1=="APICURIO_ADMIN_PASSWORD" {print $2}' /root/apicurio-credentials.txt

The Explore page lists the groups. The screenshot at the top of this guide shows default, which holds the Confluent-compatible subjects, and the demo group registered in Step 6. Open a group and then an artifact to see its metadata and versions, with tabs for its rules, branches, contract and usage:

Each version has its own page with its metadata, state and comments, and tabs for the documentation, the content and the references:

The registry enforces the roles, not the page. The UI shows every action to every user, and the registry refuses an action that the user's role does not allow with an error (for example, reader cannot create or change an artifact). Global rules and the registry's settings are under Global rules and Settings (admin):

There is no sign-out button, because the browser keeps the HTTP login until you close it. Use a private window for a short session, or close the browser when you are done.

6

Register and fetch a schema with the REST API (v3)

The core REST API is at /apis/registry/v3. The registry serves its reference documentation at https://<public-ip>/apis/registry/v3, behind the same login. Register a JSON Schema as developer. The FIND_OR_CREATE_VERSION option makes the call safe to repeat:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt)
sudo curl -s --cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-developer.curl \
  -H 'Content-Type: application/json' --data @- \
  "https://${H}/apis/registry/v3/groups/demo/artifacts?ifExists=FIND_OR_CREATE_VERSION" <<'JSON' | python3 -m json.tool {"artifactId": "user-signup", "artifactType": "JSON", "name": "User signup event", "firstVersion": {"version": "1.0.0", "content": {"contentType": "application/json", "content": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"title\":\"UserSignup\",\"type\":\"object\",\"properties\":{\"userId\":{\"type\":\"string\"},\"email\":{\"type\":\"string\"}},\"required\":[\"userId\"]}"}}} JSON

The registry answers with the artifact and its first version (your ids and times differ):

{ "artifact": { "owner": "developer", "createdOn": "2026-10-29T08:51:12Z", "modifiedBy": "developer", "modifiedOn": "2026-10-29T08:51:12Z", "artifactType": "JSON", "groupId": "demo", "artifactId": "user-signup", "name": "User signup event" }, "version": { "version": "1.0.0", "owner": "developer", "createdOn": "2026-10-29T08:51:12Z", "artifactType": "JSON", "globalId": 1, "state": "ENABLED", "groupId": "demo", "contentId": 1, "artifactId": "user-signup" } }

Fetch the latest content and list the group as reader:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt) R=(--cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-reader.curl)
sudo curl -s "${R[@]}" "https://${H}/apis/registry/v3/groups/demo/artifacts/user-signup/versions/branch=latest/content"; echo
sudo curl -s "${R[@]}" "https://${H}/apis/registry/v3/groups/demo/artifacts" | python3 -c 'import json,sys; [print(a["artifactId"], a["artifactType"]) for a in json.load(sys.stdin)["artifacts"]]'
{"$schema":"https://json-schema.org/draft/2020-12/schema","title":"UserSignup","type":"object","properties":{"userId":{"type":"string"},"email":{"type":"string"}},"required":["userId"]} user-signup JSON

reader cannot change anything. The registry refuses the write with a 403:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt)
sudo curl -s -o /dev/null -w '%{http_code}\n' --cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-reader.curl \
  -H 'Content-Type: application/json' --data '{"artifactId":"not-allowed","artifactType":"JSON","firstVersion":{"content":{"contentType":"application/json","content":"{}"}}}' \
  "https://${H}/apis/registry/v3/groups/demo/artifacts"
403
7

Use the Confluent-compatible Schema Registry API

The Confluent-compatible API is at /apis/ccompat/v7. This is the URL that Kafka clients built for Confluent Schema Registry use (Step 10). A subject is an artifact in the default group.

Register an Avro schema for the orders topic's values as developer and set the subject's compatibility to BACKWARD. Then check two changes against it as reader:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt) D=(--cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-developer.curl -H 'Content-Type: application/vnd.schemaregistry.v1+json') R=(--cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-reader.curl -H 'Content-Type: application/vnd.schemaregistry.v1+json') C="https://${H}/apis/ccompat/v7"
sudo curl -s "${D[@]}" --data '{"schema":"{\"type\":\"record\",\"name\":\"Order\",\"namespace\":\"demo\",\"fields\":[{\"name\":\"orderId\",\"type\":\"string\"},{\"name\":\"total\",\"type\":\"double\"}]}"}' "${C}/subjects/orders-value/versions"; echo
sudo curl -s "${D[@]}" -X PUT --data '{"compatibility":"BACKWARD"}' "${C}/config/orders-value"; echo
# adds an optional field (a default value): backward compatible
sudo curl -s "${R[@]}" --data '{"schema":"{\"type\":\"record\",\"name\":\"Order\",\"namespace\":\"demo\",\"fields\":[{\"name\":\"orderId\",\"type\":\"string\"},{\"name\":\"total\",\"type\":\"double\"},{\"name\":\"currency\",\"type\":\"string\",\"default\":\"EUR\"}]}"}' "${C}/compatibility/subjects/orders-value/versions/latest"; echo
# changes a field's type: not compatible sudo curl -s "${R[@]}" --data '{"schema":"{\"type\":\"record\",\"name\":\"Order\",\"namespace\":\"demo\",\"fields\":[{\"name\":\"orderId\",\"type\":\"long\"},{\"name\":\"total\",\"type\":\"double\"}]}"}' "${C}/compatibility/subjects/orders-value/versions/latest"; echo sudo curl -s "${R[@]}" "${C}/subjects"; echo
{"id":2} {"compatibility":"BACKWARD","normalize":false} {"is_compatible":true} {"is_compatible":false} ["orders-value"]

With the rule in place, the registry also refuses to register an incompatible version (HTTP 409), so a producer cannot publish a schema that would break its consumers.

8

Artifact rules

The registry checks rules whenever new content is added. The rule types are Validity (the content parses as its type), Compatibility (against earlier versions: BACKWARD, FORWARD, FULL and their transitive forms) and Integrity (references resolve).

You can set rules globally (admin), per group or per artifact. Enable a global Validity rule as admin and list the global rules:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt) A=(--cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-admin.curl)
sudo curl -s -o /dev/null -w '%{http_code}\n' "${A[@]}" -H 'Content-Type: application/json' --data '{"ruleType":"VALIDITY","config":"FULL"}' "https://${H}/apis/registry/v3/admin/rules"
sudo curl -s "${A[@]}" "https://${H}/apis/registry/v3/admin/rules"; echo
204 ["VALIDITY"]

A second run answers 409 for the first call, because the rule already exists. Set an artifact rule the same way under /apis/registry/v3/groups/<group>/artifacts/<artifact>/rules, or in the artifact's Rules panel in the UI.

9

Users and roles

User Role Can

admin sr-admin everything: artifacts, global rules, settings, role management, export and import

developer sr-developer read, register and change artifacts, versions, rules and metadata

reader sr-readonly read only

Give each application its own least-privilege login. A consumer that only looks schemas up needs readonly. A producer that registers schemas, or your CI pipeline, needs developer. Keep admin for administration.

sudo apicurio-user list
admin sr-admin developer sr-developer reader sr-readonly

Add a user (here a read-only login for a consumer application), re-key a password, or remove a user. apicurio-user generates the password on the instance and stores it in /root/apicurio-credentials.txt and in a curl config /root/apicurio-<name>.curl. It updates both the front door and the registry's user store, and it restarts the registry, which takes a few seconds:

sudo apicurio-user add orders-consumer readonly
sudo apicurio-user passwd orders-consumer
sudo apicurio-user remove orders-consumer

You cannot remove the last admin. The command never takes a password on the command line. Read the new password from the credentials file (APICURIO_ORDERS_CONSUMER_PASSWORD).

10

Connect Kafka clients

Kafka producers and consumers talk to the registry over HTTPS with one of the logins. They pin this instance's certificate in the client's own trust file, never in an operating-system or Java-wide trust store. Copy the certificate to the client machine and check its fingerprint against APICURIO_TLS_CERT_SHA256 before you use it:

scp -i <your-key.pem> ubuntu@<public-ip>:/etc/sac/apicurio-registry.pem ./apicurio-registry.pem openssl x509 -in ./apicurio-registry.pem -noout -fingerprint -sha256

Apicurio Registry SerDes (io.apicurio:apicurio-registry-avro-serde-kafka and the Protobuf and JSON Schema equivalents) read the certificate file directly:

apicurio.registry.url=https://<public-ip>/apis/registry/v3 apicurio.registry.auth.username=developer apicurio.registry.auth.password=<APICURIO_DEVELOPER_PASSWORD> apicurio.registry.tls.truststore.type=PEM apicurio.registry.tls.truststore.location=/path/to/apicurio-registry.pem
# producers: register new schemas automatically (needs the developer role) apicurio.registry.auto-register=true

Confluent serializers (io.confluent:kafka-avro-serializer and friends) use the Confluent-compatible API. Create a client-own PKCS12 trust file that holds only this certificate, and choose your own trust-file password:

keytool -importcert -noprompt -alias apicurio-registry -file ./apicurio-registry.pem \
  -keystore ./apicurio-client-truststore.p12 -storetype PKCS12 -storepass '<choose-a-password>'
schema.registry.url=https://<public-ip>/apis/ccompat/v7 basic.auth.credentials.source=USER_INFO basic.auth.user.info=reader:<APICURIO_READER_PASSWORD> schema.registry.ssl.truststore.location=/path/to/apicurio-client-truststore.p12 schema.registry.ssl.truststore.password=<choose-a-password> schema.registry.ssl.truststore.type=PKCS12

Keep these files readable by the application only, because they hold a login. Use reader for consumers and developer for producers that register schemas. To use the instance's DNS name instead of its IP address, see Step 12.

Kafka itself is a separate service (for example, the Apache Kafka versions in this listing). The registry only stores and serves the schemas.

11

Export and import, and the browser guard

An export downloads everything in the registry (artifacts, versions, content, rules and settings) as a ZIP file. An import loads such a file into an empty registry. Both are admin operations, and in the UI they are under Settings.

From a client, an export must carry the same-origin signal that a browser sends from the registry's own page (Sec-Fetch-Site: same-origin). Without it, the registry refuses the request, so a link on another web site cannot make your browser download your registry:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt) A=(--cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" -K /root/apicurio-admin.curl)
sudo curl -s -o /dev/null -w 'no browser signal: %{http_code}\n' "${A[@]}" "https://${H}/apis/registry/v3/admin/export"
sudo curl -s -o /dev/null -w 'cross-site: %{http_code}\n' "${A[@]}" -H 'Sec-Fetch-Site: cross-site' "https://${H}/apis/registry/v3/admin/export"
sudo curl -s -o /tmp/apicurio-export.zip -w 'same-origin: %{http_code}\n' "${A[@]}" -H 'Sec-Fetch-Site: same-origin' "https://${H}/apis/registry/v3/admin/export" python3 -c 'import sys,zipfile; z=zipfile.ZipFile(sys.argv[1]); print(len(z.namelist()), "entries in the export")' /tmp/apicurio-export.zip
no browser signal: 403 cross-site: 403 same-origin: 200 14 entries in the export

The same guard covers the one-time download links that the UI uses for exports, and the per-version and contract exports. To import a ZIP into a new, empty instance, use POST /apis/registry/v3/admin/import (Content-Type: application/zip, admin login).

12

Your own host name and certificate

To serve the registry under a DNS name that you own, first point an A record at the instance or its Elastic IP. Then run:

sudo apicurio-set-hostname registry.example.com

This command issues a new self-signed certificate for the name (plus the instance's own names and addresses). It pins nginx and the browser guards to the certificate, and it updates the URLs and the fingerprint in the credentials file.

To use a certificate from your certificate authority (or Let's Encrypt) instead, copy the certificate chain (server certificate first) and its unencrypted private key to the instance, and then install them:

sudo apicurio-install-cert /path/to/fullchain.pem /path/to/privkey.pem registry.example.com

The boot-time renewal never replaces an installed certificate. Run sudo apicurio-set-hostname <name> to go back to a self-signed one. With a CA-issued certificate, Kafka clients can trust it the normal way, and the client-own trust file from Step 10 is no longer needed.

13

Back up the registry's data

The registry's data lives in its PostgreSQL database apicurio. Back it up with pg_dump, run as the postgres administrator over the local socket:

sudo -u postgres pg_dump -Fc apicurio > /var/backups/apicurio-$(date +%F).dump

To restore into this or a new instance of the same version, stop the registry, recreate the database empty, restore the dump, and then start the registry:

sudo systemctl stop apicurio-registry
sudo -u postgres psql -c 'DROP DATABASE apicurio' -c 'CREATE DATABASE apicurio OWNER apicurio'
sudo -u postgres pg_restore -d apicurio --no-owner --role=apicurio /var/backups/apicurio-<date>.dump
sudo systemctl start apicurio-registry

An export ZIP (Step 11) is a second, version-independent backup. Copy backups off the instance (for example, to Amazon S3), and keep /root/apicurio-credentials.txt with them. EBS snapshots of the volume also work.

14

Instance metadata and outbound connections

The registry and PostgreSQL can reach nothing but localhost. nginx and the boot service can reach anything except the instance metadata service:

for u in apicurio-registry postgresql@16-main nginx apicurio-firstboot; do printf '%s: ' "$u"; systemctl show -p IPAddressDeny --value "$u"; done
apicurio-registry: 0.0.0.0/0 ::/0 postgresql@16-main: 0.0.0.0/0 ::/0 nginx: 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128 apicurio-firstboot: 169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128

The registry and PostgreSQL units add IPAddressAllow=localhost. Nothing in the registry's default configuration connects out. If you switch on a feature that does, such as publishing registry events to Kafka, allow just that destination with a drop-in and keep the metadata address denied. For example, run sudo systemctl edit apicurio-registry with:

[Service] IPAddressAllow=10.0.12.0/24
15

Service management, logs and health

systemctl show apicurio-registry -p ActiveState -p SubState -p User --no-pager sudo journalctl -u apicurio-registry -b --no-pager -o cat --grep 'started in' -n 1
User=apicurio ActiveState=active SubState=running 2026-10+00-29 08:43:26 INFO [io.quarkus.bootstrap.runner.Timing] (main) apicurio-registry-app 3.3.3 on JVM (powered by Quarkus 3.33.3.1) started in 13.300s. Listening on: http://127.0.0.1:8080. Management interface listening on http://127.0.0.1:9000.

The registry logs to the journal (journalctl -u apicurio-registry), including an access log line per request and an audit line per change (apicurio.audit). nginx logs to /var/log/nginx/, and PostgreSQL logs to /var/log/postgresql/.

The registry's configuration is in /etc/apicurio-registry/registry.properties. It holds no secrets, and the per-instance passwords are in secrets.properties next to it. After a change, run sudo systemctl restart apicurio-registry.

/healthz answers without a login, with the body ok, only while the registry reports ready. Use it for load balancer health checks:

H=$(sudo sed -n 's/^APICURIO_HOST=//p' /root/apicurio-credentials.txt)
curl -s --cacert /etc/sac/apicurio-registry.pem --resolve "${H}:443:127.0.0.1" "https://${H}/healthz"
ok

The self-check re-verifies the whole appliance with this instance's own logins. It covers the services, the listening set, the login wall and the roles, a schema round trip with a compatibility check, the browser guards, the metadata block and the certificate:

sudo apicurio-selfcheck | tail -1
SELFCHECK OK - Apicurio Registry 3.3.3 on loopback behind HTTPS, per-instance logins at the front door and in the registry (roles enforced), origin and cross-site guards, exact listening set, metadata block verified.

Support

SecureAnyCloud provides support for this image by email at sales@secureanycloud.com. Include the instance ID, the region and the output of sudo apicurio-selfcheck. The Apicurio Registry documentation is at https://www.apicur.io/registry/docs/.

Apicurio is a trademark of Red Hat, Inc. SecureAnyCloud is not affiliated with or endorsed by Red Hat or the Apicurio project. Kafka is a trademark of the Apache Software Foundation. Confluent is a trademark of Confluent, Inc.

Deploy on AWS

Launch Apicurio Registry 3.3.3 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

Deploy on AWS