Skip to main content
Web is in alpha. To add it to your organization, email [email protected].
A Web build is either a bundle built by Revyl or a registered deployment URL. Managed builds run your recipe locally or on Revyl’s build runners, then upload a verified bundle. URL builds open an existing preview deployment. You choose and configure your deployment host; Revyl doesn’t generate a host-specific CI workflow. For Next.js and static Vite recipes and test-only sign-in, start with the Web framework guide.

Create a Web app

In the dashboard, click Create app, pick Web, and optionally enter your first deployed HTTPS URL and commit SHA. Web apps don’t need a package or bundle ID. To add a URL later, click Upload build on the Builds page, pick the Web app, and enter the HTTPS URL. From the CLI:
revyl init doesn’t detect Web frameworks, package managers, or preview hosts, create a Web app, or write a Web recipe. Add the app to .revyl/config.yaml yourself:
This app-ID-only profile supports URL registration and session start. A Web-only build needs no build.framework, and a Web recipe can sit beside ios and android recipes.

Build a Web bundle

Add a recipe with your project’s package manager and build command:
The default runs remotely; --remote is also accepted. --local uses your installed toolchain and still uploads the result. Both paths use the same recipe, app association, and build lifecycle. Remote builds require a Git commit and record the original HEAD even when the source includes local edits. output_path must be one dedicated project-relative output directory, without globs. Only its contents enter the .tar.zst bundle, not the source checkout. Symlinks, special files, .git, .revyl, .env*, and credential files are rejected. Compressed and unpacked data are each limited to 200 MiB, with at most 20,000 archive entries and 1,024-byte paths. Static output needs an index.html at its root. revyl build requires explicit build_commands and output_path; it never guesses a build command or turns an app-only profile into a recipe. Building a URL-only profile fails with profile <name> is URL-only; add build_commands and output_path, or register previews with revyl build upload --url.

Server-rendered apps

Package the server and all of its runtime dependencies inside the output directory, and give a start command relative to the bundle root:
The server must listen on all interfaces (0.0.0.0) at the configured port, not only localhost or 127.0.0.1; the browser reaches it at http://revyl-app.localhost:<port>. Revyl sets HOSTNAME=0.0.0.0 and PORT for the process. The app is ready when GET <healthcheck> returns any 2xx or 3xx status; redirects aren’t followed. A localhost-only app fails the health check with couldn't connect to revyl-app:<port>. The defaults are port 3000, healthcheck /, and a 60-second startup timeout (10 to 600 seconds allowed). Without run.start, the session serves static output. A request for a missing path without a file extension gets <path>.html when that file exists, otherwise the root index.html, so client-side routes and clean URLs load on reload or deep link. A missing asset such as /app.js still returns 404. run.env_keys names existing plaintext organization launch variables, and run.secret_names names encrypted ones. These are names, never values or KEY=VALUE assignments. They’re injected only into the app process when the session starts it, not into the browser or the build. Build-time settings stay in env and secrets, as for mobile recipes. Never bake secrets into the output directory.

Register a preview from CI

  • App. --app <name-or-id> selects the Web app. Without it, the command reads build.profiles.<name>.web.app_id. Profiles that name different Web apps need --app.
  • URL. Must be HTTPS, with no credentials, query string, or fragment. Revyl doesn’t fetch anything when it registers the URL.
  • Pull request. The command stores the same CI metadata on the build as mobile uploads do. GitHub Actions and Buildkite are detected automatically. On any other runner, pass --repo owner/name --commit <full PR head SHA>, and add --pr <number> to choose the pull request explicitly. --commit requires --repo.
  • Branch. --branch <name> registers the build on that Revyl branch, creating it if needed; without it, the build lands on the app’s default branch. Give each pull request its own branch (for example --branch pr-42) so previews never become what the default branch opens.
  • Latest. Registering the same URL and commit again returns the existing build ("was_reused": true) and makes it its branch’s latest.
--file, --header, --version, --pin, --set-current, --no-set-current, --preview, and --name are rejected by build upload --platform web. pr_review.build.profile can name a profile with a complete Web build recipe. Naming a URL-only Web profile there is rejected with the same URL-only error.

Open a build

This opens the newest Web build on the app’s default branch, using the app from config. --branch <name> opens the newest build on that branch, --all-branches the newest across every branch, and --build-version-id a specific build. --url registers a URL (on --branch when given) and opens it in one step. --build-selection inherit and pinned aren’t available for Web. See Sessions to start one from the dashboard.

When a bundle’s app fails

If a bundle’s app can’t start, or exits during the session, the session fails with a specific reason, for example “Your app exited before it was ready (exit code 1)”, a health check that didn’t return 2xx or 3xx in time, a bundle that didn’t match the build’s size or checksum, or a missing launch variable named in run.env_keys or run.secret_names. The CLI, MCP tools, and the session’s failure state in the app all show the reason. Revyl keeps the last 64 KiB of the app’s combined stdout and stderr. Members of the organization see the last lines in the live session’s failure state and in the session report, and can download the whole log from either. Public report links show only the reason. Values of the variables injected through run.env_keys and run.secret_names, and token-shaped strings such as JWTs and signed-URL signatures, are redacted before the output leaves the session.