Overview
The web app is the only deployable open-source component. It ships two ways from the same codebase:- a Docker image published to GitHub Container Registry, built by CI
- a Cloudflare Workers bundle built with OpenNext, deployed manually
You don’t need any of this to contribute. It’s here so you understand what happens after
your pull request merges, and why CI builds two different bundles.
Continuous integration
.github/workflows/ci.yml runs on every push and pull request to main and develop, on
Node 22 with pnpm 10:
pnpm build (the standalone Next.js output) and pnpm worker:build (the Cloudflare
bundle) run, so a change that breaks either target fails the pull request.
Publishing
Public environment variables are baked in at image build time as Docker build args, supplied
from repository secrets. Because they’re compiled into the client bundle, an image is tied to
the environment it was built for.
Staging passes
NEXT_PUBLIC_API_URL, NEXT_PUBLIC_FRONTEND_URL and
NEXT_PUBLIC_METADATA_ASSETS_S3_BUCKET; production currently passes only
NEXT_PUBLIC_API_URL.
The Docker image
Dockerfile is a three-stage build on node:22-alpine:
- deps: installs with
pnpm i --frozen-lockfile(git hooks disabled viaHUSKY=0) - builder: runs
pnpm build, which emitsoutput: "standalone" - runner: copies the standalone server plus static assets, runs as a non-root
nextjsuser, listens on port3000
Cloudflare Workers
open-next.config.ts enables static-asset incremental caching and cache interception.
wrangler.toml defines the worker (nodejs_compat, a 60s CPU limit, and an ASSETS binding
served from .open-next/assets).
The extra step in worker:build is scripts/copy-learn-html.js, which copies the prerendered
/learn chapter HTML into .open-next/assets/learn so Cloudflare serves those pages directly
as static assets.
The API
The API is a separate, closed-source service deployed independently athttps://api.opensource-together.com. Its contract is documented in the
API Reference, and contributors develop against the
local mock instead.