Self-hosted service
bewcloud/bewcloud avatar
bewcloud/bewcloud

bewCloud: a small self-hosted sync server written in Deno

A simpler alternative to Nextcloud and ownCloud, built with TypeScript and Deno. 🦕

1,241 stars60 forksTypeScriptAGPL-3.0

At a glance

What is it?
bewCloud is a TypeScript file sync and sharing server built on Deno, deployed with docker compose alongside Postgres and Radicale, and offered as a simpler alternative to Nextcloud and ownCloud. The trade-off is explicit: you get files, calendars and contacts from a codebase you can read, and you give up the enormous plugin ecosystem of the projects it replaces.
Who is it for?
bewCloud suits someone who wants a self-hosted sync server they can read end to end, who is comfortable with docker compose and Postgres, and who needs files plus CalDav and CardDav without the weight of Nextcloud or ownCloud. It does not suit an organisation that depends on the wider ecosystem those projects have, and it does not suit a first time server operator, because the sample configuration is explicitly described as working but very unsafe.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A sync server written in Deno rather than a PHP platform

bewCloud presents itself as a simpler alternative to Nextcloud and ownCloud, and the stack is where the difference starts. There is no PHP runtime and no long dependency tree of plugins. The backend is TypeScript on Deno, the frontend components are TSX, styling goes through Tailwind, and the whole thing is packaged as a container image served by docker compose. The stated ambition is narrow: a file sync and sharing server for a household or small group, not a platform to build on. That framing shows up in the code layout, which is small enough to describe in a short list. Backend routes live in routes.ts, public files in public/, pages in pages/, JSX and TSX components in components/, cron jobs in crons/, reusable code in lib/, and database migrations in db-migrations/. The repository is mirrored to Codeberg under a Forgejo instance as well as GitHub, and the last push was on 2026-10-18, with v4.19.0 published on 2026-10-12, following v4.18.0 and v4.17.0 on the two days before that. Desktop and mobile clients live in separate repositories, bewcloud-desktop and bewcloud-mobile, so the core repository is only the server.

The three containers behind a single compose file

The compose file defines three services and one volume, and reading it tells you most of what the application needs. The website service pulls a published image and binds to the loopback address only.

yaml
services:
  website:
    image: ghcr.io/bewcloud/bewcloud:v4.19.0
    restart: always
    ports:
      - 127.0.0.1:8000:8000
    mem_limit: '256m'

The postgresql service runs postgres:18.6 with a named volume for its data directory, and the radicale service runs tomsquest/docker-radicale:3.7.6.0 for CalDav, CardDav and contacts. Radicale is hardened inside the compose file: it runs with a read only root filesystem, no-new-privileges, and all capabilities dropped except a small allow list, and its data and config directories are mounted separately with the config mounted read only. Each service carries a 256m memory limit. Two details are commented out but worth knowing about, because they are the ones you uncomment when you need external access. The database port mapping is off by default, so the container is not reachable from outside, and the same applies to the Radicale port. The web service is bound to 127.0.0.1 rather than 0.0.0.0, which means the app is not directly exposed to your network until you put a reverse proxy in front of it.

First run: directories, config files, migration

The self-hosting instructions are short and worth following in order. You create three local directories first, and the comment explains what each is for.

sh
mkdir data-files data-radicale radicale-config

The last two are only needed if you are using CalDav, CardDav and contacts. Next you copy four configuration files from the repository into place: docker-compose.yml, .env.sample saved as .env, bewcloud.config.sample.ts saved as bewcloud.config.ts, and the radicale config saved as radicale-config/config. The documentation is blunt about the result of not editing them, saying that no changes should yield a working but very unsafe setup. After that, two commands bring the stack up and initialise the database.

sh
docker compose up -d
docker compose run --rm website bash -c "cd /app && make migrate-db"

The app is then available at http://localhost:8000, and the migration only needs to run on the first start and after data updates. A practical wrinkle the documentation flags is permissions: the container runs as a user with id 1993, taken from the Deno docker image, and if you hit permission errors on the data-files directory the suggested fix is to chown that directory to 1993:1993. The author notes that this id comes from the upstream Deno image and may change in future, which is a good reason to check the current value rather than paste it blindly.

The .env file where the secrets and the database connection live

Everything environment specific sits in .env, and the sample file is short enough to read end to end before you deploy.

bash
PORT=8000

POSTGRESQL_HOST="postgresql"
POSTGRESQL_USER="postgres"
POSTGRESQL_PASSWORD="fake"
POSTGRESQL_DBNAME="bewcloud"
POSTGRESQL_PORT=5432
bash
JWT_SECRET="fake"
PASSWORD_SALT="fake"

MFA_KEY="fake"
MFA_SALT="fake"

OIDC_CLIENT_ID="fake"
OIDC_CLIENT_SECRET="fake"

The literal value fake in the sample is a placeholder and every one of these lines needs replacing before the instance is reachable by anyone else. The optional entries are optional in the sense that the feature they enable is off unless you fill them in: MFA_KEY and MFA_SALT turn on multi-factor authentication, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET enable single sign on, and SMTP_USERNAME and SMTP_PASSWORD are needed for signup email verification or for multi-factor through an authenticated email service. POSTGRESQL_HOST is described as a container name or an external hostname, so pointing the same app at a managed Postgres is a configuration change rather than a code change. The FAQ file in the repository is the place the documentation points for more on .env based configuration.

A TypeScript config file and the first signup that becomes admin

Application level settings live in bewcloud.config.ts rather than in the environment file, which keeps the two concerns separate. The file is copied from bewcloud.config.sample.ts and, again, the documentation warns that leaving it untouched gives you a working but unsafe setup. One behaviour deserves attention before you point a browser at the instance, and the project calls it out with an important note: even with signups disabled through config.auth.allowSignups=false, the first signup will work and that account becomes an admin. That is a deliberate bootstrap path, and it is also a race if the instance is reachable before you claim it. The rest of the configuration surface is described in the FAQ file, which covers private calendar and file sharing among other topics. The security model relies on the runtime configuration and the secrets in .env together, so the pair has to be reviewed as one unit rather than separately, and the AGPL-3.0 licence governs how the server may be modified and redistributed.

Development workflow: make targets over Deno tasks

Contributors work through a Makefile whose targets are thin wrappers around Deno tasks, which is the pattern the project uses to keep the commands identical for everyone.

sh
make migrate-db
make start
make format
make test
make build

make start runs the app in development mode and watches CSS files for changes, make build generates CSS for production, make format formats the code, and make test runs the checks and the test suite. Two less used targets exist for day to day work, make preview, which serves the app in production mode from the built files, and make exec-db, which opens psql inside the Postgres container for direct queries. Local development needs the Deno version stated in the .dvmrc file, and Docker with docker compose for the Postgres dependency, and there is a docker-compose.dev.yml for running a local database. The Dockerfile shows how the production image is put together: it starts from denoland/deno:ubuntu-2.9.7, installs make, zip and coreutils, builds the frontend and CSS with deno task build, caches main.ts so the app does not need recompiling at startup, and runs as the deno user rather than root. Deployment, in the simplest sense, is pushing to the main branch.

Where bewCloud is the wrong tool

The clearest case against is a requirement for the wider Nextcloud or ownCloud ecosystem. If your plan depends on a specific client, an app from a large app store, or a feature set beyond files, calendars and contacts, the simpler alternative does not cover it, and the value of the rewrite disappears. A second case is a team that will not operate a Postgres instance and a Radicale container, because the compose file is the deployment model and there is no hosted tier in the repository. The third is operational maturity. The compose file is convincing, but the sample configuration is described by its own author as working but very unsafe, and the first signup admin behaviour needs to be handled deliberately. A fourth consideration is the release rhythm. v4.17.0, v4.18.0 and v4.19.0 landed on consecutive days in October 2026, which means the project moves fast and that pinning a version means accepting a fairly young release, or you track main and absorb the churn. Community packaging exists outside the repository, with a NixOS integration and a Helm chart offered by third parties, which helps if Docker is not how you deploy, but the project states these are not officially endorsed. If you need an enterprise contract, an audit trail or a support commitment, the upstream projects are the only realistic starting point.

Editorial conclusion

bewCloud suits someone who wants a self-hosted sync server they can read end to end, who is comfortable with docker compose and Postgres, and who needs files plus CalDav and CardDav without the weight of Nextcloud or ownCloud. It does not suit an organisation that depends on the wider ecosystem those projects have, and it does not suit a first time server operator, because the sample configuration is explicitly described as working but very unsafe. Before you expose it, change JWT_SECRET, PASSWORD_SALT, the Postgres password and the OIDC secrets in .env, note that the first signup becomes an admin even when allowSignups is false in the config, and read FAQ.md for the .env based configuration options.

Frequently asked questions

How do I self-host bewCloud?

Create the data-files, data-radicale and radicale-config directories, copy docker-compose.yml, .env.sample as .env, bewcloud.config.sample.ts as bewcloud.config.ts and the radicale config, then run docker compose up -d and the migrate-db command. The app is then available at http://localhost:8000.

Does bewCloud support CalDav and CardDav?

Yes. A Radicale container is part of the compose file for CalDav, CardDav and contacts, and its data and config directories are the two extra local folders the setup instructions ask you to create.

What licence is bewCloud released under?

The repository declares the AGPL-3.0 licence, with the LICENSE file at the top level. Anyone modifying and serving the software should read the licence text rather than rely on a summary.

How do I set up single sign on and multi-factor authentication in bewCloud?

Both are enabled through .env. Filling in OIDC_CLIENT_ID and OIDC_CLIENT_SECRET turns on SSO, and MFA_KEY and MFA_SALT turn on multi-factor authentication, and both are optional in the sample file.

Official sources

  1. bewcloud/bewcloud on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bewcloud-bewcloud.svg)](https://hysenlabs.com/projects/bewcloud-bewcloud)

Was this useful?