Websurfx on Ubuntu 24.04 on Azure User Guide
Overview
Websurfx is a free, open source, privacy-respecting metasearch engine written in Rust. Instead of profiling you, it forwards your query to several upstream engines (DuckDuckGo, Wikipedia, Brave, Mojeek and Bing on this image), aggregates and de-duplicates the results, and returns them without storing your search history, setting tracking cookies or building an advertising profile. The cloudimg image compiles Websurfx from a pinned upstream release into a single hardened binary, runs it as a non-root websurfx systemd service bound to loopback, backs it with an on-box Redis result cache, and fronts it with an nginx reverse proxy that terminates TLS and adds a per-VM HTTP Basic Auth gate so the instance is never left open as a public search proxy. A unique Basic Auth password and a self-signed TLS certificate are generated on the first boot of every VM. Backed by 24/7 cloudimg support.
What is included:
- Websurfx (pinned upstream release v1.29.9) compiled as a single Rust binary and run as the
websurfxsystemd service, bound to loopback127.0.0.1:8080 - An nginx reverse proxy terminating TLS on
:443and adding a per-VM HTTP Basic Auth gate (useradmin) with a unique password generated on first boot;:80serves the health probe and redirects everything else to HTTPS - A per-VM self-signed TLS certificate generated on first boot (SAN set to the instance public IP)
- An on-box Redis result cache bound to loopback
127.0.0.1:6379(the upstream hybrid cache: Redis first, in-process memory fallback) so repeat searches are fast, never reachable off the VM - The JSON search API enabled (
?json=true) alongside the HTML interface for scripting and integrations websurfx.service,nginx.serviceandredis-server.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - 24/7 cloudimg support
Websurfx is licensed under the GNU Affero General Public License v3 (AGPL-3.0). If you modify Websurfx and offer the modified version to users over a network, the AGPL requires you to make your modified source available to those users. Running the unmodified cloudimg image places no source-distribution obligation on you.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; Websurfx is light on resources at runtime. NSG inbound: allow 22/tcp from your management network, 443/tcp for the web interface, and 80/tcp for the health probe and the HTTPS redirect. The image ships a self-signed certificate; for production, put your own domain and a CA or Let's Encrypt certificate in front of nginx (see Maintenance).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Websurfx 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), HTTPS (443) and HTTP (80). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name websurfx \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name websurfx --port 443 --priority 1010
az vm open-port --resource-group <your-rg> --name websurfx --port 80 --priority 1020
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active websurfx.service nginx.service redis-server.service
All three report active. Websurfx runs as the dedicated non-root websurfx user, bound to the loopback listener 127.0.0.1:8080; nginx fronts it on :443 with TLS and the per-VM HTTP Basic Auth gate, and exposes an unauthenticated /healthz; Redis provides the on-box result cache on loopback 127.0.0.1:6379.

Step 5 - Retrieve your password
nginx protects Websurfx with HTTP Basic Auth over TLS. The username is admin and a unique password is generated on the first boot of your VM and written to a root-only file:
sudo cat /root/websurfx-credentials.txt
This file contains WEBSURFX_USERNAME, WEBSURFX_PASSWORD and the WEBSURFX_URL to open in a browser. The password is stored on disk only as a bcrypt hash in /etc/nginx/websurfx.htpasswd, so no plaintext password ships in the image. Store the password somewhere safe.

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm authentication and the JSON search API
Because a password is set on first boot, an unauthenticated request returns HTTP 401, so nobody reaches your instance without the password. The following reads the per-VM password from the credentials file and proves the round-trip - unauthenticated is rejected, the correct password authenticates over TLS, and an authenticated JSON search returns aggregated results (the -k flag accepts the self-signed certificate):
PW=$(sudo grep '^WEBSURFX_PASSWORD=' /root/websurfx-credentials.txt | cut -d= -f2-)
echo "unauth : $(curl -sk -o /dev/null -w '%{http_code}' https://127.0.0.1/)"
echo "authed : $(curl -sk -o /dev/null -w '%{http_code}' -u admin:$PW https://127.0.0.1/)"
curl -sk -u admin:$PW 'https://127.0.0.1/search?q=privacy&json=true' \
| python3 -c 'import sys,json; d=json.load(sys.stdin); print("results:", len(d.get("results", [])))'
It prints unauth : 401, then authed : 200, then a non-zero result count. The ?json=true endpoint is enabled on this image so you can drive Websurfx from scripts and integrations; the same query without it returns the HTML results page.

Step 8 - Sign in and open the search page
Browse to https://<vm-public-ip>/. Your browser warns once about the self-signed certificate - accept it (or install your own certificate per Maintenance). It then prompts for a username and password: enter admin and the password from Step 5. Websurfx opens on its clean search home - a single search box with no ads, no tracking and no cookies set until you choose to save preferences.

Step 9 - Run a private search
Type a query and press Enter. Websurfx queries the upstream engines in parallel, aggregates and de-duplicates the results, and shows them with per-result engine attribution. Repeat queries are served from the on-box Redis cache for speed. Your query is never logged or tied to a profile.

Step 10 - Tune engines and preferences
Open Settings to tailor the instance. From here you choose the colour scheme and theme, set your safe-search level, and enable or disable any of the upstream engines. Preferences are stored in your browser (or an opt-in cookie), so different users of the same instance can keep their own settings.

Step 11 - Review the About page
The About page describes what Websurfx is and links to the project documentation, so users of your instance understand the privacy model of the metasearch engine they are using.

Step 12 - Review the configuration and cache
Websurfx's configuration lives at /etc/xdg/websurfx/config.lua, and the on-box Redis cache is bound to loopback:
grep -E 'binding_ip|^port|redis_url|safe_search' /etc/xdg/websurfx/config.lua
systemctl is-active redis-server.service
redis-cli -h 127.0.0.1 ping
The config binds Websurfx to loopback 127.0.0.1:8080, points the cache at redis://127.0.0.1:6379, and sets the safe-search level. Redis answers PONG on loopback only. To repoint Websurfx at a different set of engines or change the theme, edit config.lua and run sudo systemctl restart websurfx.

Maintenance
- Change the password: regenerate the Basic Auth file with
sudo htpasswd -B /etc/nginx/websurfx.htpasswd admin, then update/root/websurfx-credentials.txtfor your records and reload nginx withsudo systemctl reload nginx. - Add engines or change settings: edit
/etc/xdg/websurfx/config.lua(theupstream_search_enginestable) and runsudo systemctl restart websurfx. - Add TLS for production: put your own domain in front of nginx and replace the self-signed certificate in
/etc/ssl/websurfx/with one from your CA or Let's Encrypt, then reload nginx. - Updates: Websurfx is pinned to a specific upstream release in this image for reproducibility. cloudimg publishes refreshed images; for security patches apply OS updates with
sudo apt update && sudo apt upgrade. - Cache: the on-box Redis cache holds search results for a short expiry. It is bound to loopback and never exposed off the VM. Flush it any time with
redis-cli flushall.
Support
This image is maintained by cloudimg with 24/7 support. For help deploying or operating Websurfx on Azure, contact cloudimg support through the Azure Marketplace listing.