docs
GitHubNL
Self-hosting

Configuration

The environment variables a self-hosted Appsweet deployment sets, and which of them are required.

Everything is set through environment variables, all of them prefixed APPSWEET_. Three of them are required before the process will start at all: APPSWEET_DATABASE_URL, APPSWEET_OIDC_ISSUER, and APPSWEET_OIDC_CLIENT_ID. The process environment wins over a .env file sitting next to the binary.

Server

Variable Requirement Description
APPSWEET_DATABASE_URL required to boot Postgres connection string. Appsweet runs its migrations automatically on boot, and the first of them needs the vector extension — see Docker Compose.
APPSWEET_DATABASE_MAX_CONNECTIONS default 5 Size of the Postgres connection pool.
APPSWEET_BIND_ADDR default 127.0.0.1:3000 Socket address the gateway listens on. Inside a container this has to be 0.0.0.0:3000, or nothing outside reaches it.
APPSWEET_PUBLIC_BASE_URL default http://localhost:3000 The URL browsers reach Appsweet on. Sign-in redirect URIs are derived from it.
APPSWEET_TRUST_X_FORWARDED default false Set to true only behind a proxy you control that sets the forwarding headers itself.
APPSWEET_CORS_ALLOWED_ORIGINS optional Comma-separated browser origins allowed to call the REST API directly. The defaults cover the desktop client.
APPSWEET_BACKEND_PROCESS_MODE default serve+worker Whether this process runs the HTTP gateway, the background worker, or both. The default is what makes one container a complete deployment; serve and worker exist for splitting them across replicas.

Sign-in

Appsweet does not manage passwords itself — authentication is OIDC, and the bundled stack runs Zitadel for it.

Both variables below are read while the process is starting up, not lazily on the first sign-in attempt. A container given only a database URL exits before it binds a port, so /ready never answers at all. This is a boot requirement, not a feature you can defer.

Variable Requirement Description
APPSWEET_OIDC_ISSUER required to boot Absolute http(s) URL of the Zitadel instance. Discovery is derived from it.
APPSWEET_OIDC_CLIENT_ID required to boot Client ID Appsweet presents to the issuer.
NOTE

The issuer has to be Zitadel. These two variables take any OIDC issuer URL and the backend will complete a sign-in against it, but the workspace it then looks up comes from Zitadel's own claims: the authorization request asks for the scope urn:zitadel:iam:user:resourceowner, and provisioning reads urn:zitadel:iam:user:resourceowner:id, …:name and …:primary_domain from the token. Without them /v1/me returns 403 workspace_claims_required and the client never gets a workspace. Mapping another provider's claims onto these is not implemented, so Keycloak, Authentik, Auth0 and Entra ID do not work today.

File storage

Uploads live in S3-compatible object storage, not on a local disk — there is no data directory to mount. Storage is required for any real deployment: the storage layer is always S3-protocol, with a configured endpoint, credentials, and bucket names.

The bundled default is Garage, which the repository's own stack brings up for you, but nothing depends on it. Any S3-compatible endpoint works — Hetzner Object Storage, Cloudflare R2, Backblaze B2, AWS S3, Wasabi, or your own Garage or SeaweedFS.

NOTE

Leaving APPSWEET_S3_ENDPOINT unset does not stop the backend booting: it starts with files disabled and everything else working, and says nothing about it in the logs. That is a development convenience for trying Appsweet out, not a supported storage-less mode. Anything other people will use needs storage configured.

Variable Requirement Description
APPSWEET_S3_ENDPOINT required Endpoint the backend uses for its own S3 operations. Unset disables files — development only.
APPSWEET_S3_PUBLIC_ENDPOINT optional Endpoint presigned URLs are minted against, for when clients cannot reach the internal one.
APPSWEET_S3_ACCESS_KEY required S3 access key.
APPSWEET_S3_SECRET_KEY required S3 secret key.
APPSWEET_S3_REGION default garage S3 region.
APPSWEET_S3_UPLOADS_BUCKET default uploads Bucket for user-uploaded files.
APPSWEET_S3_BACKUPS_BUCKET default backups Bucket for backups, kept separate so it can carry its own retention.
APPSWEET_S3_DERIVED_BUCKET default derived Bucket for regenerable artefacts such as thumbnails.
APPSWEET_S3_PRESIGN_TTL_SECS default 900 Lifetime of a presigned upload or download URL, in seconds.

Create the buckets yourself

Naming a bucket here does not create it. Appsweet's storage layer only ever issues object operations — put, get, head, delete, presign — and never CreateBucket. All three buckets have to exist before the first file operation, and the key in APPSWEET_S3_ACCESS_KEY needs read and write on each of them.

Nothing tells you at boot. With APPSWEET_S3_ENDPOINT set the backend comes up with files enabled and reports itself ready; the missing bucket only surfaces when someone uploads or downloads something, as an error from the storage provider.

That is exactly the work the bundled stack does for you: it runs a one-shot garage-init service that waits for Garage, then creates $APPSWEET_S3_UPLOADS_BUCKET, $APPSWEET_S3_BACKUPS_BUCKET and $APPSWEET_S3_DERIVED_BUCKET and grants $APPSWEET_S3_ACCESS_KEY read, write and owner on each. On your own S3 provider, do the equivalent by hand or in your provisioning before you point Appsweet at it.

Sweetheart

NOTE

There is nothing here to configure. Sweetheart is designed to run on-device inside the desktop app, and the backend reads no AI-related environment variables at all. Sweetheart and models explains what that means and what changes later.

Back to appsweet.appEdit this page on GitHub