YASAD runs as one Docker image or as a Cloudflare Worker. The image does not handle TLS: put a reverse proxy in front of it. Every variable is described in configuration.md, and the themes YASAD ships are listed in its Themes section.
Caddy gets and renews the HTTPS certificate by itself. You need a domain name whose DNS record points to the machine, with ports 80 and 443 open to the internet.
-
Download the two example files into a new folder:
mkdir yasad && cd yasad curl -fsSLO https://raw.githubusercontent.com/TheIceMagmaCube/yasad/main/docs/examples/caddy/compose.yaml curl -fsSLO https://raw.githubusercontent.com/TheIceMagmaCube/yasad/main/docs/examples/caddy/Caddyfile
compose.yaml runs YASAD and Caddy. YASAD publishes no port: only Caddy reaches it, and
TRUST_PROXYis1.portal.example.org { reverse_proxy yasad:3000 } -
Replace
portal.example.orgin the Caddyfile with your domain. -
Create the
.envfile, with your domain inPUBLIC_URL:cat > .env <<EOF AUTH_SECRET=$(openssl rand -base64 32) SETUP_CODE=$(openssl rand -base64 24) PUBLIC_URL=https://portal.example.org EOF
-
Start:
docker compose up -d. -
Open
https://portal.example.organd follow the first install from the README: setup code, administrator, catalog. Then remove theSETUP_CODEline from.envand rundocker compose up -dagain.
Any reverse proxy works. This section assumes Nginx Proxy Manager runs in Docker, with Cloudflare in front of it.
Put YASAD on Nginx Proxy Manager's Docker network and publish no port. Find that network's name with docker network ls (often <folder of Nginx Proxy Manager>_default), then use this compose.yaml, with that name in place of npm_default:
services:
yasad:
image: ghcr.io/theicemagmacube/yasad:latest
env_file: .env
environment:
AUTH_SECRET: ${AUTH_SECRET:?set AUTH_SECRET in .env (openssl rand -base64 32)}
PUBLIC_URL: ${PUBLIC_URL:?set PUBLIC_URL in .env, e.g. https://portal.example.org}
SETUP_CODE: ${SETUP_CODE:-}
# No ports: only Nginx Proxy Manager reaches YASAD.
networks:
- npm
volumes:
- yasad-data:/data
restart: unless-stopped
networks:
npm:
external: true
name: npm_default
volumes:
yasad-data:Create the .env file as in the Caddy steps above, and add TRUST_PROXY=2 to it. Then carry over these settings:
- the proxy host forwards to hostname
yasad, port3000, schemehttp; - in Cloudflare, set SSL/TLS to Full (strict) and give the proxy host a certificate for the domain (Let's Encrypt, or a Cloudflare Origin certificate). With Flexible, traffic between Cloudflare and Nginx Proxy Manager is not encrypted, and forcing SSL in Nginx Proxy Manager then loops;
PUBLIC_URLis the address people open, for examplehttps://portal.example.org;TRUST_PROXY=2, since two proxies stand in front of YASAD. With1, YASAD sees the address of a Cloudflare server instead of the visitor's, so many visitors share one IP and the setup and sign-in limits block them together;- do not publish port 3000 to the internet: with
TRUST_PROXYset, a client that reaches YASAD directly could forge its IP; - Nginx Proxy Manager must accept traffic only from Cloudflare (allow only Cloudflare's IP ranges on the firewall, or use a Cloudflare Tunnel). Otherwise a client that bypasses Cloudflare can forge its IP too. Docker publishes ports ahead of host firewalls such as ufw, which then do not filter them: filter in the
DOCKER-USERiptables chain or in your hosting provider's firewall, or use a Cloudflare Tunnel and publish no port.
The same code runs on Cloudflare Workers, with a D1 database. You need a Cloudflare account, Git and Node 24. Use the main branch, which holds the last stable release (beta holds unreleased work).
-
Get the code and install the dependencies, then go to
backend/:git clone -b main https://github.com/TheIceMagmaCube/yasad.git cd yasad npm ci cd backend
Run every following command from
backend/, except step 5. -
Sign in to Cloudflare.
npx wrangler whoamithen shows your account.npx wrangler login
wrangler loginopens a browser. On a machine without one, for example a server reached over SSH, create an API token in the Cloudflare dashboard instead (template Edit Cloudflare Workers, plus the D1 Edit permission if the template lacks it) and export it:export CLOUDFLARE_API_TOKEN=.... With several accounts, also exportCLOUDFLARE_ACCOUNT_ID. -
Create the database:
npx wrangler d1 create yasad
It prints a
database_id. When wrangler offers to add it to the configuration for you, answer no. Put the id inbackend/wrangler.jsonc, ind1_databases[0].database_id. Keep the bindingDBandmigrations_dirdrizzle. -
Create the tables:
npx wrangler d1 migrations apply yasad --remote
-
Build the frontend, at the root of the repository. The Worker serves
frontend/distas static assets.cd .. npm run build --workspace frontend cd backend
-
Deploy, from
backend/. It prints the address of the Worker,https://yasad.<your-subdomain>.workers.dev. The first upload of the static files can take a minute.npm run deploy:worker
It sets the version from
git describe --tags --always, like the Docker image, then runswrangler deploy. Arguments after--go towrangler deploy, for examplenpm run deploy:worker -- --config <file>.The deploy also registers the cron trigger that runs the link checks every minute (status checks). To run one locally:
npm run dev:worker -- --test-scheduled, thencurl "http://localhost:8787/__scheduled?cron=*+*+*+*+*". -
Set the secrets. Each command asks for the value.
npx wrangler secret put AUTH_SECRET # output of: openssl rand -base64 32 npx wrangler secret put PUBLIC_URL # the workers.dev address or your custom domain, in https npx wrangler secret put SETUP_CODE # output of: openssl rand -base64 24
To use your own domain, add it to the Worker in the Cloudflare dashboard (Settings, Domains & Routes): no file changes. Set
PUBLIC_URLto that address and always open YASAD there: sign-in from theworkers.devaddress is then refused, since it is another origin.Set any other variable the same way, for example
PASSWORD_SIGN_INor theOIDC_*ones. Do not setCATALOG_FILEorTRUST_PROXYon Workers:CATALOG_FILEwould block catalog imports and edits without loading any file, and the client IP always comes fromCF-Connecting-IP. -
Check that it answers:
https://<your-worker>/api/healthreturns{"ok":true,"version":"0.2.0","database":true}(the version from step 6, the release tag without itsvonmain;devonly when deployed without git), and/api/setupreturns{"open":true}. -
Open
PUBLIC_URLand follow the first install from the README. -
Once setup is done, delete the setup code:
npx wrangler secret delete SETUP_CODE
-
At the root of the repository, get the new code. Your
database_idis a local change tobackend/wrangler.jsonc, so set it aside during the pull:git stash git pull git stash pop
If
git stash popreports a conflict, editbackend/wrangler.jsoncto keep the new content with yourdatabase_id, then rungit resetandgit stash drop. -
Run
npm ciat the root, then repeat steps 4, 5 and 6: apply the new migrations, build the frontend, deploy.
docker compose pull
docker compose up -dYASAD applies the database migrations at every start.
Setup opens again only when SETUP_CODE is set and no enabled local administrator exists. While one exists, any other administrator, local or from the OIDC provider, can reset its password from the Users screen (Reset password). An administrator cannot reset their own password there.
YASAD has no way to recover the password of the only administrator. Create a second administrator early, for example an OIDC one through OIDC_ADMIN_GROUPS, so that one can always reset the other.