Streaming & Messaging Azure

Svix on Ubuntu 24.04 on Azure User Guide

| Product: Svix on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Svix on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Svix is the open source webhooks as a service server: a JWT authenticated REST API that sends, signs, queues, retries and manages the outbound webhooks your application delivers to your customers' endpoints. Instead of building and operating a reliable webhook pipeline yourself, you create applications and endpoints and send messages through the API, and Svix handles signing, delivery, exponential backoff retries and delivery observability.

The cloudimg image ships the free and open source, MIT licensed svix-server (a Rust binary), run the officially supported way as the upstream container pinned by image digest, alongside a bundled PostgreSQL for durable state and Redis for the delivery queue and cache. All three images are pinned by digest and captured into the VM, so your instance starts in seconds. Because Svix is a control plane for outbound events, nothing ships with a known secret: a unique JWT signing secret, a unique database password, a unique Redis password and a unique bootstrap API token are generated for each VM on first boot, before the port is reachable. Backed by 24/7 cloudimg support.

Svix is a trademark of its respective owner. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by Svix. It ships the free and open source MIT licensed self hosted software, unmodified.

The svix-server version, the docker, svix and nginx services active, the compose containers running, and the listeners showing nginx on port 80 and the svix API bound to loopback only

What is included:

  • Svix server v1.98.0 (the MIT licensed svix-server Rust binary), pinned by image digest
  • A bundled PostgreSQL 16 database, pinned by image digest, reachable only inside a private Docker network (never published to a host port)
  • A bundled Redis 7 queue and cache, pinned by image digest, password protected and reachable only inside the private Docker network
  • Docker Engine with the svix API published to the loopback interface only, fronted by nginx on port 80
  • svix.service, svix-firstboot.service, svix-postboot.service and nginx.service as systemd units, enabled and active on boot
  • A unique JWT signing secret, a unique PostgreSQL password, a unique Redis password and a unique bootstrap API token generated per VM on first boot, never baked into the image
  • A clean, empty database on first boot: no default login, no shipped secret, no prior data
  • Ubuntu 24.04 LTS base with latest security patches applied at build time
  • Azure Linux Agent for seamless cloud integration and SSH key injection
  • 24/7 cloudimg support with guaranteed 24 hour response SLA

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region
  • Subscription to the Svix listing on Azure Marketplace

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point. For higher webhook volumes use Standard_D2s_v5 or larger. NSG inbound: allow 22/tcp from your management network and 80/tcp for the API from the networks that call it.

Step 1: Deploy from the Azure Portal

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Svix by cloudimg, and select Create. On Basics 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). Then Review + create and Create.

Step 2: Deploy from the Azure CLI

RG="svix-prod"; LOCATION="eastus"; VM_NAME="svix-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/svix-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name svix-vnet --address-prefix 10.100.0.0/16 --subnet-name svix-subnet --subnet-prefix 10.100.1.0/24
az network nsg create -g "$RG" --name svix-nsg
az network nsg rule create -g "$RG" --nsg-name svix-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name svix-nsg --name allow-http --priority 110 \
  --destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name svix-vnet --subnet svix-subnet --nsg svix-nsg --public-ip-sku Standard

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

Step 4: Confirm the services are running

The stack runs as three containers (svix-server, PostgreSQL and Redis) under one svix.service, fronted by nginx. Confirm the services are active, see the containers, and check which ports are bound. nginx listens on :80, the svix API is published to 127.0.0.1:8071 (loopback only), and PostgreSQL and Redis have no host port at all:

sudo systemctl is-active docker svix nginx
sudo docker compose -f /etc/svix/compose.yaml ps
sudo ss -tlnp | grep -E ':80 |:8071'

You will see all three services report active, the three containers running, and nginx bound on :80 with the svix API bound only on 127.0.0.1:8071. PostgreSQL (5432) and Redis (6379) never appear on a host port because they are reachable only inside the private Docker network.

The svix-server version pinned by digest, the docker, svix and nginx services all active, the compose containers running, and ss showing nginx on port 80 and the svix API on loopback 127.0.0.1:8071 with PostgreSQL and Redis on no host port

Step 5: Read the per instance credentials

A unique JWT signing secret, database password, Redis password and a bootstrap API token were generated for this VM on the first boot, before the port was reachable, and written to a root only file. Read them:

sudo cat /root/svix-credentials.txt

The file (mode 0600 root:root) holds SVIX_JWT_SECRET, the bootstrap API_TOKEN you authenticate the API with, POSTGRES_PASSWORD, REDIS_PASSWORD, and SVIX_URL (the address to reach this VM on). None of these ship in the image; every value is unique to this VM, so no two instances share a credential and there is no default token to change.

The svix-credentials.txt file at mode 0600 root root with the per VM JWT signing secret, bootstrap API token, PostgreSQL password, Redis password and URL all masked, and a note that they are generated uniquely on first boot before the port is public

Step 6: Verify the API security model

The health endpoint is public, but every other endpoint requires the bearer token. Confirm that an unauthenticated request is rejected with 401, while the per instance token authenticates a real call:

SVIX_TOKEN=$(sudo grep '^API_TOKEN=' /root/svix-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'health    (no auth):    %{http_code}\n' http://localhost/api/v1/health/
curl -s -o /dev/null -w 'list apps (no token):   %{http_code}\n' http://localhost/api/v1/app/
curl -s -o /dev/null -w 'list apps (with token): %{http_code}\n' -H "Authorization: Bearer $SVIX_TOKEN" http://localhost/api/v1/app/

The health check returns 200 with no authentication, the unauthenticated application list is rejected with 401, and the same call with the per instance token returns 200. This is the whole security model: the health probe is open for load balancers, and everything else is gated by the bearer token.

curl showing the health endpoint returning HTTP 200 with no auth, the application list rejected with HTTP 401 for no token and a wrong token, and HTTP 200 when the per instance API token is supplied

Step 7: Send your first webhook

The core workflow is: create an application (one per customer or tenant), register the event types you send, add endpoints (the URLs Svix delivers to), then send messages. Svix signs each message, queues it and delivers it to every matching endpoint, retrying with exponential backoff on failure. Run the whole flow from the VM:

SVIX_TOKEN=$(sudo grep '^API_TOKEN=' /root/svix-credentials.txt | cut -d= -f2-)
APP_ID=$(curl -s -X POST http://localhost/api/v1/app/ \
  -H "Authorization: Bearer $SVIX_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"orders-service"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
echo "application: $APP_ID"
curl -s -X POST http://localhost/api/v1/event-type/ \
  -H "Authorization: Bearer $SVIX_TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"order.created","description":"An order was created"}' >/dev/null
curl -s -X POST http://localhost/api/v1/app/$APP_ID/endpoint/ \
  -H "Authorization: Bearer $SVIX_TOKEN" -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/webhook","description":"partner endpoint"}' >/dev/null
curl -s -X POST http://localhost/api/v1/app/$APP_ID/msg/ \
  -H "Authorization: Bearer $SVIX_TOKEN" -H 'Content-Type: application/json' \
  -d '{"eventType":"order.created","payload":{"orderId":"ord_123","amount":4200}}'
echo

Each call returns the created object as JSON. The final POST .../msg/ returns a message with an id beginning msg_ and your payload echoed back; Svix has accepted the event, signed it, and queued it for delivery to the https://example.com/webhook endpoint with automatic retries. Point the endpoint URL at a real receiver of yours (for a quick test, a request bin) and you will see the signed delivery arrive.

Running the webhook workflow over the API: create an application, register the order.created event type, add a webhook endpoint, and send a message that Svix signs, queues and delivers to the endpoint with retries

Step 8: Call the API from your own machine

From anywhere that can reach the VM on port 80, use the bootstrap token and the VM public IP. Retrieve the token once (over SSH) and then call the API with your VM address:

curl -H "Authorization: Bearer <API_TOKEN>" http://<vm-ip>/api/v1/app/

For production, use the official Svix client libraries (available for Python, JavaScript/TypeScript, Go, Rust, Java, C#, Ruby, PHP and more). Point the library's server_url at http://<vm-ip>/ (or your own domain, see Step 10) and pass the bootstrap token, then create applications, endpoints and messages from your application code exactly as the curl calls above do.

Step 9: Mint additional API tokens

The bootstrap token is scoped to one organization. To issue additional tokens (for example one per environment or per tenant boundary), generate them with the same per instance JWT secret:

sudo docker run --rm --env-file /etc/svix/svix.env -e SVIX_QUEUE_TYPE=redis -e SVIX_CACHE_TYPE=redis \
  svix/svix-server svix-server jwt generate

This prints a new Token (Bearer): ... value signed with this VM's JWT secret. Store it securely; anyone holding a token can call the API, so treat tokens like passwords and rotate the JWT secret (in /etc/svix/svix.env, then sudo systemctl restart svix) if one is ever exposed.

Step 10: Use your own domain and HTTPS (production)

The image serves the API over plain HTTP on port 80, which is convenient behind a load balancer or private network. For production you should terminate TLS in front of Svix so tokens and payloads travel encrypted:

  • Front the VM with Azure Application Gateway, a load balancer with a managed certificate, or a CDN, and forward to nginx on port 80.
  • Or install your own certificate: add a TLS server block to /etc/nginx/sites-available/svix referencing your certificate and key, open 443/tcp on the NSG, then sudo systemctl reload nginx.
  • Point a DNS name you control at the VM public IP and use that name (and https://) as the server_url in your Svix client libraries.

Step 11: Server components

Component Version / Detail
Webhooks server svix-server v1.98.0 (Rust binary, pinned by image digest)
API front nginx on :80 reverse proxying to 127.0.0.1:8071
Database bundled PostgreSQL 16 (private Docker network, no host port)
Queue and cache bundled Redis 7 (private Docker network, no host port, password protected)
Orchestration Docker Compose under svix.service
JWT secret per instance, generated first boot into /etc/svix/svix.env
Bootstrap token per instance, minted first boot into /root/svix-credentials.txt
Operating system Ubuntu 24.04 LTS (patched at build)
License MIT (Svix)

Step 12: Managing the Svix service

sudo systemctl status svix --no-pager | head -12
sudo docker compose -f /etc/svix/compose.yaml logs --tail 40 svix-server

Restart the whole stack with sudo systemctl restart svix, follow the API logs live with sudo docker compose -f /etc/svix/compose.yaml logs -f svix-server, and view configuration in /etc/svix/svix.env and /etc/svix/compose.yaml. After changing the environment file, restart the service to apply it.

Step 13: Security recommendations

  • Restrict the NSG. Allow TCP 80 (and 22 for admin) only from the networks that call the API.
  • Terminate TLS in front of Svix (Step 10) so bearer tokens and payloads are encrypted in transit.
  • Treat tokens like passwords. They are stored in /root/svix-credentials.txt (root only); rotate the JWT secret if a token is ever exposed.
  • Keep the bundled database and queue private. PostgreSQL and Redis are on a private Docker network with no host port; keep them that way.
  • Keep the OS patched. Unattended security upgrades remain enabled on the running VM.

Step 14: Support and Licensing

Svix is distributed under the MIT License. This cloudimg image bundles the unmodified official open source svix-server release; cloudimg provides the packaging, the bundled PostgreSQL and Redis, the per instance secret generation and bootstrap token, the paired deploy guide, and 24/7 support with a guaranteed 24 hour response SLA. Svix is an independent open source project and this image is not affiliated with or endorsed by Svix.

Deploy on Azure

Find Svix on Ubuntu 24.04 on the Azure Marketplace, published by cloudimg. Deploy from the Portal or the Azure CLI as shown above.

Need Help?

Email support@cloudimg.co.uk for deployment help, configuration questions, or licensing enquiries.