Deployment guide

deepstream

Overview

deepstream.io is an open-source realtime data sync and publish/subscribe server. This guide covers deploying and configuring deepstream.io on Ubuntu 24.04 on Azure, using SecureAnyCloud Azure Marketplace images. Clients hold a single websocket connection and use four primitives over it:

  • Records are JSON documents that synchronise automatically, so every subscriber sees a change the moment it is written.
  • Events are a straightforward publish and subscribe channel for fire-and-forget messages.
  • RPCs route a request to whichever client registered to answer it, and return the result.
  • Presence reports which clients are currently connected.

The image installs deepstream.io 10.1.4 (the @deepstream/server package) on Node.js 22 LTS from the official NodeSource repository. It runs under systemd as deepstream.service.

deepstream is a headless server: there is no web interface and no dashboard to log into. You interact with it from your own application code, from the HTTP API, and from the command line.

Secure by default. This is the main difference from a stock install. Upstream's sample configuration sets auth: type: none, which means any client that can reach the port may read and write every record, event and RPC. Upstream's sample user file also seeds a published example account. This image ships neither.

The image is pinned to file-based authentication with salted password hashes. The user database ships empty, and a unique administrator password is generated on the first boot of every VM. The file auth handler rejects any username it does not find, so an instance fails closed. It refuses every client until first boot has provisioned a credential.

Proof that it works, not just that it is up. A realtime broker that answers a health check but never delivers a message is functionally dead. The image therefore ships deepstream-selftest, which opens two independent authenticated connections, publishes from one, and requires the other to actually receive the payload. It also runs four authentication probes, including a positive control.

What you get:

  • deepstream.io 10.1.4 from the official @deepstream/server npm package on Node.js 22 LTS, run under systemd as deepstream.service
  • File-based authentication replacing upstream's open default, with a per-VM admin password generated on first boot and written to a root-only file
  • A reverse proxy on port 80 that handles websocket upgrade, so the realtime endpoint and the HTTP API are reachable on a standard port while deepstream itself binds to loopback only
  • The HTTP and JSON API on /api for systems that cannot hold an open websocket
  • An unauthenticated liveness endpoint on /health-check, suitable for Azure health probes and load balancers
  • deepstream-selftest, a two-connection pub/sub and authentication proof that you can run at any time
  • @deepstream/client installed, so the self test and your own scripts work on the VM immediately

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet plus subnet in the target region
  • A subscription to the deepstream.io listing on Azure Marketplace
  • Network security group rules that allow TCP 22 (admin) and TCP 80 (the realtime websocket endpoint and HTTP API). Open port 80 only to the networks your application clients connect from

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) comfortably handles development and modest production workloads.

deepstream holds record state in memory. Size RAM to your working set, and step up to a compute-optimised size for high connection counts.

1

Deploy from the Azure Portal

  • Open the deepstream.io offer on the Azure Marketplace and choose Get It Now, then Create.
  • Select your subscription, resource group and region, and choose the Standard_B2s size.
  • Provide your SSH public key for the azureuser account.
  • In the network security group, allow inbound TCP 22 from your admin network and TCP 80 from your application network. Then create the VM.
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:deepstream-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-rg \
  --name deepstream-01 \
  --image sac1647283583153:deepstream-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard
3

First boot

On the first boot of every VM, deepstream-firstboot.service generates a random administrator password unique to that instance. It hashes the password using deepstream's own file auth settings, writes the hash into /etc/deepstream/users.yml, and records the plaintext in /root/deepstream-credentials.txt. Only then does it create the marker that releases deepstream.service to start.

That ordering is deliberate. deepstream.service carries a condition on that marker. If the first boot step ever failed, the server would decline to start rather than come up with an empty user database. First boot completes in a few seconds.

4

Confirm the server is running

SSH in as azureuser. Confirm that both services are active, check the version, and see where each service is listening:

systemctl is-active deepstream.service nginx.service deepstream --version ss -tln | grep -E ':6020 |:80 '

You should see active twice and version 10.1.4.

Note that deepstream itself is bound to 127.0.0.1:6020 only. The reverse proxy on port 80 is the single public listener.

5

Retrieve your per-VM credentials

The administrator password is unique to this VM, and only root can read it:

sudo cat /root/deepstream-credentials.txt

The file gives you the username (admin), the password, and the websocket URL your clients should connect to. Store the password in your own secret manager, and treat the file as sensitive.

6

Check liveness

/health-check is unauthenticated and reports only whether the server is alive. It is the right target for an Azure health probe or a load balancer. Query it with:

curl -s -o /dev/null -w 'health-check -> HTTP %{http_code}\n' http://127.0.0.1/health-check

You should see HTTP 200. This endpoint deliberately reveals nothing about your data.

7

Prove that strangers are refused

Run this check before you put anything real on the server. It runs four probes. The correct credential must be accepted. This positive control stops a server that is simply down from passing as a secure one. A wrong password, an unknown username and a wholly unauthenticated connection must each be refused:

sudo deepstream-selftest authz

All four lines must report PASS, and the command must exit 0.

8

Prove that messages actually flow

This check opens two independent authenticated connections. It subscribes the first connection to an event and a record, publishes both from the second, and requires the first to receive them with a matching nonce:

sudo deepstream-selftest pubsub

A matching nonce on both lines proves that the broker routed a real payload between two separate clients. It is not merely a sign that a port was open.

9

Use the HTTP and JSON API

Applications that cannot hold an open websocket can publish and read over HTTP. Every request carries authData, so the same credential gates this transport too.

Write a value into a record:

curl -s -X POST http://127.0.0.1/api -H 'Content-Type: application/json' -d '{ "authData": {"username":"admin","password":"<DEEPSTREAM_ADMIN_PASSWORD>"}, "body": [ {"topic":"record","action":"write","recordName":"guide/demo","path":"status","data":"ok"} ] }'

Expected output:

{"result":"SUCCESS","body":[{"success":true}]}

Now read it back:

curl -s -X POST http://127.0.0.1/api -H 'Content-Type: application/json' -d '{ "authData": {"username":"admin","password":"<DEEPSTREAM_ADMIN_PASSWORD>"}, "body": [ {"topic":"record","action":"read","recordName":"guide/demo"} ] }'

The expected output is the value you just wrote, with its version:

{"result":"SUCCESS","body":[{"version":1,"data":{"status":"ok"},"success":true}]}

The same request without credentials is rejected outright:

curl -s -o /dev/null -w 'no credentials -> HTTP %{http_code}\n' -X POST http://127.0.0.1/api \
  -H 'Content-Type: application/json' \
  -d '{"body":[{"topic":"record","action":"read","recordName":"guide/demo"}]}'

Expected output:

no credentials -> HTTP 401
10

Connect from your own application

Install the client in your project and connect to the VM's public address on port 80. Replace <vm-public-ip> with your VM's address, and use the password from Step 5:

npm install @deepstream/client
const { DeepstreamClient } = require('@deepstream/client') const client = new DeepstreamClient('ws://<vm-public-ip>/deepstream') await client.login({ username: 'admin', password: '<your-password>' }) // Events: fire and forget publish/subscribe client.event.subscribe('telemetry/temperature', (data) => console.log(data)) client.event.emit('telemetry/temperature', { celsius: 21.5 }) // Records: a JSON document that stays in sync across every subscriber const record = client.record.getRecord('devices/thermostat-1') record.subscribe((data) => console.log('thermostat changed:', data)) await client.record.setDataWithAck('devices/thermostat-1', { celsius: 21.5 })

Any client that connects without valid credentials is disconnected before it can read or write anything.

11

Add more users

The image ships a single admin account. To add another user, generate a password hash and add it to the user database:

sudo deepstream hash 'your-new-password'

The command prints a salted digest. Add the digest to /etc/deepstream/users.yml, following the shape of the existing entry. Then restart the server so that it loads the new user database:

# Add to /etc/deepstream/users.yml:
# reporting-service:
# password: "<the digest printed above>"
# serverData:
# role: service
# then apply:
sudo systemctl restart deepstream

Note that deepstream 10.1.4 silently ignores the -c / --config flag because of a regression in a bundled dependency. The DEEPSTREAM_CONFIG_DIRECTORY environment variable selects the config file instead.

That variable is already set system-wide on this image, which is why the plain deepstream hash command above works correctly.

12

Restrict what each user may do

Every authenticated user currently has full access to all records, events and RPCs. To grant narrower rights per user or per name pattern, edit the rules in /etc/deepstream/permissions.yml and restart the server. This example is illustrative, so adapt the patterns to your data model:

# In /etc/deepstream/permissions.yml, replace the "*" record rule with
# something narrower, for example:
# record:
# "devices/$deviceId":
# read: true
# write: "user.data.role === 'admin'"
# then apply:
sudo systemctl restart deepstream
13

Durability and scaling

By default, deepstream keeps record data in memory, so records do not survive a restart. That is the right default for presence, telemetry and ephemeral collaborative state.

If you need records to persist, enable a cache and a storage backend. Uncomment the cache and storage blocks in /etc/deepstream/config.yml and point them at Redis and MongoDB respectively, then restart. Clustering multiple deepstream nodes also uses Redis. Both are external services that you run and secure yourself.

14

Security recommendations

  • Open port 80 only to the networks your clients connect from. deepstream is bound to loopback and the reverse proxy is the only public listener, so the network security group is your access boundary.
  • Terminate TLS in front of the server before you send credentials over an untrusted network. The websocket login sends the password to the server, so put a certificate on the proxy. For anything beyond a private network, connect with wss:// rather than ws://.
  • Rotate the shipped admin credential once you have created per-application accounts. Give each application its own user rather than sharing admin.
  • Tighten /etc/deepstream/permissions.yml so that each account can touch only the records and events it needs. The default grants every authenticated user full access.
  • Keep /root/deepstream-credentials.txt restricted to root, and copy the password into your own secret manager.
  • Keep the OS patched. The image ships fully updated and with unattended security upgrades enabled.
15

Support and licensing

deepstream.io is free and open-source software distributed under the MIT License. This image bundles deepstream.io unmodified, and the MIT License governs your use of it. When this image was built, the project was actively maintained, with version 10.1.4 released on 30 June 2026.

SecureAnyCloud images include 24/7 support. If you need help deploying or configuring deepstream.io on Azure, contact us.

Deploy on Azure

Find this image on the Azure Marketplace and deploy in minutes.

Need Help?

Email sales@secureanycloud.com and our team will help you get up and running.

Deploy on Azure

Launch deepstream.io 10.1.4 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

Deploy on Azure