bewCloud 4.21.0 on AWS User Guide
Overview
This guide covers launching and using the bewCloud 4.21.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg file sharing and sync listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
bewCloud is an open source, self hosted personal cloud. It gives you a clean web interface for your files (upload, browse, search, share), notes, photos, a news reader, expenses and a dashboard, plus calendars and contacts. It speaks standard WebDAV, CalDAV and CardDAV, so your files, calendars and address books sync with the bewCloud desktop and mobile apps or any compatible client, and everything stays on infrastructure you control.
The cloudimg image locks bewCloud down:
- HTTPS from the first boot. nginx serves bewCloud on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS, and requests for a host name the instance does not serve are dropped.
- An administrator unique to your instance, created before the site is reachable. bewCloud makes the first person to sign up an administrator. On first boot the image creates the administrator itself, with a random password written to
/root/bewcloud-credentials.txt(root only), and only then publishes the site. Sign ups are closed, so nobody can register an account on your instance. - No shared secret in the image. The image ships with no database, no application secret, no administrator and no TLS key. First boot creates the PostgreSQL cluster and generates bewCloud's JWT secret, password salt and MFA keys on your instance.
- No database password at all. PostgreSQL listens on its unix socket only and bewCloud connects with peer authentication, so there is no database password to leak.
- Nothing else on the network. bewCloud listens on a unix socket behind nginx, the bundled Radicale calendar and contacts server listens on loopback and only bewCloud's own account can reach it, and PostgreSQL has no TCP listener. The security group needs only 22, 80 and 443.
- Your data on its own volume. Files, calendars, contacts and the database live on a separate 20 GiB EBS data volume mounted at
/var/lib/bewcloud, which you can size at launch. - No service can reach the metadata service. bewCloud, Radicale, PostgreSQL and nginx are each blocked from the EC2 instance metadata service at the kernel level, and the AMI requires IMDSv2.
- Cross site protection. Another web site cannot make your signed in browser change your data, and it cannot sign you out either.
- Native services, current components. bewCloud runs on Deno 2.9.8 as a native systemd service on the patched Ubuntu base, not in upstream's older container image, with its mail library updated past every published advisory.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: bewCloud 4.21.0 (/opt/bewcloud/app), Deno 2.9.8, Radicale 3.8.3, PostgreSQL 16, nginx, Ubuntu 24.04 LTS, 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 with a route to the internet (or a private route you use for management).
- A security group that allows TCP 22 (SSH), TCP 443 (bewCloud, WebDAV, CalDAV and CardDAV) and TCP 80 (redirects to HTTPS) from your own networks only.
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick bewCloud 4.21.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair, a security group as above, and a subnet. Under Configure storage the AMI brings two volumes: the 8 GiB root volume and the 20 GiB data volume for your files and database. Raise the data volume's size there if you expect to store more.
With the AWS CLI (replace the values in angle brackets; the second mapping sets the data volume to 100 GiB):
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 \
--block-device-mappings 'DeviceName=/dev/sdf,Ebs={VolumeSize=100,VolumeType=gp3}' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bewcloud}]'
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| bewCloud 4.21.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot creates the database cluster on the data volume, generates the secrets and the administrator, then links the site. It takes well under a minute:
systemctl is-active bewcloud-firstboot bewcloud bewcloud-radicale postgresql@16-main nginx
active
active
active
active
active
Only SSH (22) and nginx (80, 443) listen on the network. Radicale (5232) listens on loopback, and the other two are Ubuntu's local DNS resolver:
sudo ss -lntp | awk 'NR>1 {print $4}' | sort -u
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:5232
127.0.0.53%lo:53
127.0.0.54:53
[::]:22
[::]:443
[::]:80
The data volume is mounted at /var/lib/bewcloud and holds the database cluster and your files:
findmnt -no SOURCE,SIZE,FSTYPE /var/lib/bewcloud; sudo ls /var/lib/bewcloud
/dev/nvme1n1 19.5G ext4
files
lost+found
postgresql
radicale
If nginx is not active, first boot has not finished (or failed, in which case the site is never published). Check systemctl status bewcloud-firstboot and journalctl -u bewcloud-firstboot.
Step 4: Get your URL and password
sudo cat /root/bewcloud-credentials.txt
The file is readable by root only and holds the address and this instance's administrator:
# bewCloud - generated on THIS instance's first boot by bewcloud-firstboot.service. Unique to this instance;
# nothing here was baked into the image. Sign in at bewcloud.url with this e-mail and password, then change the
# password under Settings. To serve another name: sudo bewcloud-set-hostname <name>
bewcloud.url=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/
bewcloud.admin.email=admin@bewcloud.local
bewcloud.admin.pass=<BEWCLOUD_ADMIN_PASSWORD>
The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too. To serve bewCloud under your own DNS name, see Step 9.
You can prove the password works from the command line. bewCloud accepts HTTP Basic authentication on WebDAV, so the correct password lists your files while a wrong one is refused:
sudo bewcloud-verify.sh
bewcloud-verify: login 200; anonymous /files 302 + /dav 401; wrong password 302/401; per-instance admin 200/207; 256 KiB WebDAV round trip byte-identical; CardDAV via Radicale 207
BEWCLOUD_ROUNDTRIP_OK
Step 5: Sign in
Open the bewcloud.url in your browser. The certificate is self signed, so your browser asks you to confirm it the first time (or install your own certificate, Step 9). bewCloud opens on its sign in screen. There is no setup wizard: your administrator already exists. Sign in with bewcloud.admin.email and bewcloud.admin.pass from Step 4.

Change the password under Settings after your first sign in.
Step 6: Files and WebDAV
Files is the heart of bewCloud. Upload files and create folders from the web interface, browse and search them, and share a file or folder with a link. Every file is stored on the data volume, under /var/lib/bewcloud/files.

Point the bewCloud desktop and mobile apps, or any WebDAV client (Windows, macOS Finder, rclone, davfs2), at https://<your-host>/dav/ with the same e-mail and password. From the instance itself, with curl (the password goes into a private curl config file, never onto the command line):
H=$(sudo sed -n 's#^bewcloud\.url=https://\([^/]*\)/$#\1#p' /root/bewcloud-credentials.txt)
E=$(sudo sed -n 's/^bewcloud\.admin\.email=//p' /root/bewcloud-credentials.txt); P=$(sudo sed -n 's/^bewcloud\.admin\.pass=//p' /root/bewcloud-credentials.txt)
( umask 077; printf 'user = "%s:%s"\n' "$E" "$P" > ~/.bewcloud-curl ); unset P
printf 'Hello from cloudimg.\n' > /tmp/hello.txt
curl -s -K ~/.bewcloud-curl --resolve "$H:443:127.0.0.1" --cacert /etc/ssl/bewcloud/bewcloud.crt -o /dev/null -w 'PUT hello.txt -> HTTP %{http_code}\n' -T /tmp/hello.txt "https://$H/dav/hello.txt"
curl -s -K ~/.bewcloud-curl --resolve "$H:443:127.0.0.1" --cacert /etc/ssl/bewcloud/bewcloud.crt "https://$H/dav/hello.txt"
rm -f ~/.bewcloud-curl /tmp/hello.txt
PUT hello.txt -> HTTP 201
Hello from cloudimg.
An HTTP 201 (created) confirms the upload; refresh the Files page and the file is there.
Step 7: Calendars, contacts and the other apps
The header switches between the built in apps: Dashboard, Files, Notes, Photos, News, Expenses, Contacts and Calendar. Calendars and contacts are served by the bundled Radicale server through bewCloud, so phones and desktop clients sync them over CalDAV at https://<your-host>/caldav/ and CardDAV at https://<your-host>/carddav/ with the same e-mail and password.


Which apps appear, and their order, is set in /opt/bewcloud/app/bewcloud.config.ts (see the bewCloud FAQ). Apply a change with sudo systemctl restart bewcloud.
Step 8: Add users and reset the administrator password
Sign ups are closed. To give someone else an account, open sign ups only for as long as you need: set allowSignups: true in /opt/bewcloud/app/bewcloud.config.ts, run sudo systemctl restart bewcloud, let them sign up at https://<your-host>/signup, then set it back to false and restart bewCloud again. Each user changes their own password, and can set a separate WebDAV/CalDAV password, under Settings.
If you lose the administrator password, generate a new one. The new password is written to /root/bewcloud-credentials.txt and the old one stops working at once:
sudo bewcloud-reset-admin-password
Step 9: Use your own host name and certificate
Point a DNS record at the instance (an Elastic IP keeps the address across stop and start), then tell bewCloud its name. This mints a new self signed certificate for that name, updates the nginx host rule and bewCloud's own address, and keeps the name across reboots:
sudo bewcloud-set-hostname cloud.example.com
To use a certificate from a public authority instead, replace /etc/ssl/bewcloud/bewcloud.crt (full chain) and /etc/ssl/bewcloud/bewcloud.key (mode 0600, owned by root) with your own and run sudo systemctl reload nginx. Running bewcloud-set-hostname again later would replace them with a new self signed pair.
Step 10: Check persistence and updates
Every service is enabled, so bewCloud returns after a reboot or a stop and start. If the public address changes, the next boot re-issues the certificate and the address for the new name (unless you set your own name in Step 9). Unattended security upgrades keep the operating system, PostgreSQL and nginx patched:
systemctl is-enabled bewcloud bewcloud-radicale bewcloud-set-host nginx postgresql unattended-upgrades
enabled
enabled
enabled
enabled
enabled
enabled
Step 11: Back up your data
Everything that matters is on the data volume: the PostgreSQL cluster, your files and your calendars and contacts. The simplest complete backup is an EBS snapshot of that volume (or AWS Backup on the instance). For a file level backup, dump the database and archive the files:
sudo -u postgres pg_dump bewcloud > bewcloud-backup.sql
sudo tar czf bewcloud-files-backup.tar.gz -C /var/lib/bewcloud files radicale
Keep /etc/bewcloud/bewcloud.env with the backup: it holds this instance's PASSWORD_SALT and application secrets, which the stored password hashes depend on.
Security notes
- No shared secret ships in the image. No database, application secret, administrator or TLS key exists until your instance's first boot generates them.
- The first sign up cannot claim your instance. The administrator is created before the site is published, and sign ups are closed.
- Fail closed. If first boot cannot complete, the site is never published: nginx has nothing to serve but closed connections.
- The database and Radicale are not exposed. PostgreSQL has no TCP listener and no password (peer authentication on its socket); Radicale listens on loopback and only bewCloud's own account can reach it. nginx strips the internal header bewCloud uses to talk to Radicale, so it cannot be forged from outside.
- No access to the metadata service. bewCloud, Radicale, PostgreSQL and nginx run with a kernel level block on the EC2 instance metadata address.
- Cross site requests are refused. A request from another web site that would change data, open a WebSocket or sign you out is refused before it reaches bewCloud. Because of this, a file whose name contains a backslash cannot be opened through the web interface or WebDAV.
- Restrict 22/tcp to your management network, and 80/443 to the networks that use bewCloud.
Architecture summary
| Component | Detail |
|---|---|
| Application | bewCloud 4.21.0 (AGPL-3.0) on Deno 2.9.8, /opt/bewcloud/app, bewcloud.service, unix socket /run/bewcloud/http.sock |
| Web server | nginx on 443 (HTTPS, per instance certificate in /etc/ssl/bewcloud), 80 redirects to HTTPS |
| Database | PostgreSQL 16, cluster on the data volume (/var/lib/bewcloud/postgresql), unix socket with peer authentication |
| CalDAV / CardDAV | Radicale 3.8.3, bewcloud-radicale.service, 127.0.0.1:5232 (bewCloud's account only) |
| Data volume | 20 GiB EBS at /var/lib/bewcloud: files, calendars and contacts, database |
| Configuration | /opt/bewcloud/app/bewcloud.config.ts; per instance secrets in /etc/bewcloud/bewcloud.env (root only) |
| Credentials | /root/bewcloud-credentials.txt, mode 0600, root only |
| First boot | bewcloud-firstboot.service; host name and certificate bewcloud-set-host.service (every boot) |
| Ports | 22 SSH, 80 redirect, 443 bewCloud, WebDAV (/dav/), CalDAV (/caldav/), CardDAV (/carddav/) |
| Base image | Ubuntu 24.04 LTS, fully patched, unattended security upgrades enabled, IMDSv2 required |
Support
cloudimg images come with 24/7 support. Email support@cloudimg.co.uk with your AWS account ID and the instance ID. For bewCloud itself, see the bewCloud project on GitHub and its FAQ.