bewCloud
Overview
bewCloud is an open-source, self-hosted personal cloud. It gives you a clean web interface for storing, browsing and sharing your files, alongside notes, photos, a dashboard, and optional CalDAV and CardDAV calendars and contacts. It speaks standard WebDAV, CalDAV and CardDAV, so your files and calendars sync with the bewCloud desktop and mobile apps or any compatible client, and everything stays on infrastructure you control.
This SecureAnyCloud image runs bewCloud 4.13.0 from its official container image, with PostgreSQL for its database and a bundled Radicale for CalDAV and CardDAV. Docker Compose orchestrates the stack behind nginx on a hardened Ubuntu 24.04 LTS base, patched at build time.
What you get:
- bewCloud 4.13.0 (Deno) served through nginx, orchestrated by Docker Compose and managed by systemd
- The bewCloud interface on
:80, requiring an administrator sign-in - A local PostgreSQL 18 database and a bundled Radicale CalDAV/CardDAV server, reachable only inside the VM
- A per-VM database password, application secrets and administrator password, generated on first boot and recorded in a root-only file
- Shipped unprovisioned, with no database and no secrets. bewCloud's own documentation notes that, even with sign-ups disabled, the first person to sign up becomes an administrator. So on the first boot of every VM, a one-shot service generates this VM's secrets, migrates a fresh database, and creates a single administrator account with a random password written to a root-only file
- Nothing bound to port 80 until first boot has provisioned and secured the instance. No instance is ever reachable from the network in an unprovisioned state where a passer-by could claim the admin account, and no secret is ever shared between customers
docker.serviceandbewcloud.serviceas enabled systemd units, so the stack returns after a reboot- 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_B2s (2 vCPU / 4 GiB RAM) is a sensible starting size for a personal or small-team cloud. Choose a larger size if many users sync large libraries.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp so that your browser and sync clients can reach bewCloud. Also allow 443/tcp once you terminate TLS on the VM.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for bewCloud 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:bewcloud-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name bewcloud \
--image sac1647283583153:bewcloud-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku StandardOpen port 80 so that your browser and sync clients can reach bewCloud:
az vm open-port --resource-group <your-rg> --name bewcloud --port 80Step 3 - Confirm the services are running
SSH in as azureuser. First boot pulls no images, because they are already in the image, and completes in well under a minute. Once it finishes, nginx binds port 80 and bewCloud is ready.
systemctl is-active docker bewcloudss -tlnp 2>/dev/null | grep ':80 ' | head -1 | sed 's/ */ /g' | cut -d' ' -f1-4Confirm that the three containers are up and check which versions are running:
sudo docker compose -f /opt/bewcloud/docker-compose.yml ps --format 'table {{.Service}}\t{{.Image}}\t{{.Status}}'If port 80 is not bound, first boot has not completed. This is deliberate and fails closed: bewCloud is never published before it has been provisioned and secured. Check progress with systemctl status bewcloud-firstboot.service and journalctl -u bewcloud-firstboot.service.
Step 4 - Retrieve this VM's administrator credentials
Every VM creates its own administrator on first boot. It writes the e-mail, the password and the sign-in address to a root-only credentials file. Read it:
sudo cat /root/bewcloud-credentials.txtThe same first boot also generated this VM's database password and application secrets. You never need those values. You can confirm that they exist for this machine without printing them, and that exactly one administrator exists:
for k in POSTGRESQL_PASSWORD JWT_SECRET PASSWORD_SALT MFA_KEY MFA_SALT; do v=$(sudo grep -E "^$k=" /opt/bewcloud/.env | cut -d= -f2-); printf "%-20s generated per VM (%d chars)\n" "$k" "${#v}"; doneprintf 'administrators on this VM: ';
sudo docker compose -f /opt/bewcloud/docker-compose.yml exec -T postgresql psql -U postgres -d bewcloud -tAc "SELECT count(*) FROM public.bewcloud_users WHERE (extra->>'is_admin')::boolean IS TRUE;" </dev/nullYou can prove from the command line that the generated password authenticates. bewCloud accepts HTTP Basic authentication, so the correct password reaches an authenticated page, while a wrong one is bounced back to the sign-in screen:
E=$(sudo grep '^bewcloud.admin.email=' /root/bewcloud-credentials.txt | cut -d= -f2-);
P=$(sudo grep '^bewcloud.admin.pass=' /root/bewcloud-credentials.txt | cut -d= -f2-); printf 'login page: HTTP %s\n' "$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/login)"; printf 'authenticated /files: HTTP %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -u "$E:$P" http://127.0.0.1/files)"; printf 'wrong password: HTTP %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -u "$E:WRONG-password" http://127.0.0.1/files)"You want to see 200 for the correct password and a 302 redirect for the wrong one.
Step 5 - Sign in to bewCloud
Browse to http://<vm-public-ip>/. bewCloud opens on its sign-in screen. There is no setup wizard, because your administrator was already created on first boot. Enter the bewcloud.admin.email and bewcloud.admin.pass values from Step 4.
Change the password from the settings menu after your first sign-in. Sign-ups are disabled by default, so you add further users deliberately instead of leaving open registration on.
Step 6 - Browse and manage your files
The Files app is the heart of bewCloud. Upload files and create folders from the web interface, browse and search them, and enable sharing per file or per directory. Every file stays on your own server.
The WebDav URL shown on the Files page is what you point a WebDAV client or the bewCloud desktop and mobile apps at, so that your files sync across your devices.
You can also upload from the command line with any WebDAV client. This example uses curl with the per-VM administrator credentials:
E=$(sudo grep '^bewcloud.admin.email=' /root/bewcloud-credentials.txt | cut -d= -f2-);
P=$(sudo grep '^bewcloud.admin.pass=' /root/bewcloud-credentials.txt | cut -d= -f2-); printf 'Hello from sac.\n' > /tmp/hello.txt; curl -s -o /dev/null -w 'PUT hello.txt -> HTTP %{http_code}\n' -u "$E:$P" -T /tmp/hello.txt http://127.0.0.1/dav/hello.txtAn HTTP 201 (created) confirms the upload. Refresh the Files page and the file appears.
Step 7 - Notes, photos, dashboard, and the other apps
bewCloud is more than files. The header switches between the built-in apps: a Dashboard with a quick notes widget, a Notes app, Photos, Expenses, and, backed by the bundled Radicale server, Contacts and Calendar. Each app is self-contained and stores its data on your instance.
Choose which apps appear, and their order, in bewCloud's configuration file at /opt/bewcloud/bewcloud.config.ts (see the bewCloud FAQ). Apply changes with sudo docker compose -f /opt/bewcloud/docker-compose.yml restart website.
Step 8 - Check persistence and image pins
The stack is enabled to return after a reboot, and the container images are pinned so that your instance is reproducible. Unattended security upgrades keep the base operating system patched. Verify each:
systemctl is-enabled docker nginx bewcloud-firstboot bewcloudsystemctl is-enabled unattended-upgradessudo docker image ls --format '{{.Repository}}:{{.Tag}}' | sortStep 9 - Enable HTTPS with Let's Encrypt
Put bewCloud behind HTTPS before exposing it to the internet, so that credentials and files are encrypted in transit. Point a DNS record at the VM, then install a certificate.
The bundled nginx proxies to bewCloud on the loopback interface, so certbot's nginx integration terminates TLS in front of it:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d cloud.example.comAfter issuing the certificate, set your instance's public address in /opt/bewcloud/bewcloud.config.ts (auth.baseUrl) to the https:// host name. Restart the app with sudo docker compose -f /opt/bewcloud/docker-compose.yml restart website.
Make sure 443/tcp is open in the NSG.
Step 10 - Back up and maintain your VM
Everything that matters lives in the PostgreSQL database and the uploaded files under /opt/bewcloud/data-files. A database dump plus that directory is a complete backup:
sudo docker compose -f /opt/bewcloud/docker-compose.yml exec -T postgresql pg_dump -U postgres bewcloud > bewcloud-backup.sql
sudo tar czf bewcloud-files-backup.tar.gz -C /opt/bewcloud data-filesKeep the .env file (/opt/bewcloud/.env) with the dump. It holds the PASSWORD_SALT and application secrets, which are needed to make sense of stored data. Store the backups somewhere safe.
Unattended security upgrades keep the operating system patched, and Azure Backup or managed disk snapshots give you whole-VM recovery.
Security notes
- No shared secret ships in the image. The image contains no database and no secrets. The database password, application secrets and administrator password are all generated on the first boot of your VM.
- The first sign-up cannot claim your instance. bewCloud makes the first sign-up an administrator. First boot creates a real administrator before the app is reachable, and sign-ups are disabled, so no passer-by can register the admin account.
- Nothing is published before it is secured. In the shipped image, nothing is bound to port 80. The public site is enabled only after first boot has provisioned bewCloud, so an unprovisioned instance is never exposed to the network.
- Fail closed. If first boot cannot complete, port 80 is never bound at all.
- Put bewCloud behind HTTPS before real traffic, and restrict
22/tcpto your management network. - The database and Radicale are not exposed. PostgreSQL and Radicale are reachable only inside the VM. nginx on port 80 is the sole network-facing service.
Architecture summary
- Application: bewCloud 4.13.0 (AGPL-3.0), ghcr.io/bewcloud/bewcloud:v4.13.0
- Orchestration: Docker Compose at /opt/bewcloud, managed by bewcloud.service
- Web server: nginx reverse proxy on :80 to the loopback bewCloud container
- Database: PostgreSQL 18, in-VM only
- CalDAV/CardDAV: Radicale 3.7.6, in-VM only
- Configuration: /opt/bewcloud/.env (mode 0640) and /opt/bewcloud/bewcloud.config.ts, written on first boot
- Credentials: /root/bewcloud-credentials.txt, mode 0600, root-only
- First boot: bewcloud-firstboot.service, sentinel /var/lib/sac/data/bewcloud-firstboot.done
- Ports: 22/tcp SSH, 80/tcp bewCloud interface, 443/tcp once TLS is configured
- Base image: Ubuntu 24.04 LTS, patched at build time, unattended security upgrades enabled
Support
SecureAnyCloud images come with 24/7 support. Email sales@secureanycloud.com with your Azure subscription ID and the VM name. For bewCloud itself, see the bewCloud project on GitHub and its FAQ.
Deploy on Azure
Launch bewCloud on Ubuntu 24.04 LTS with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com