Preview apps

An app task runs a long-lived process that serves traffic on a URL under rwx.run. Apps spin up on demand and spin down when idle, making them well-suited for previewing branch changes before merge — whether the reviewer is a human or an agent.

An app task looks like this:

tasks:
  - key: my-app
    use: [build]
    app:
      endpoint: my-app-pr-${{ init.pull-request-number }}
      port: 3000
    run: pnpm start
  • The endpoint will be part of the app's subdomain, and must be alphanumeric or hyphens (no underscores).
  • The port is what will bind to your server process, and must be between 1024 and 49151.

Startup timeout

By default, an app is given 1 minute to start up. After this, the app's task is marked as a failure. You can override this by configuring start-timeout:

tasks:
  - key: my-app
    use: [build]
    app:
      endpoint: my-app-pr-${{ init.pull-request-number }}
      port: 3000
      start-timeout: 2m
    run: pnpm start

Idle timeout

An app spins down after 10 minutes of inactivity by default. Set idle-timeout in the app configuration to change this duration. It must be between 10 minutes and 24 hours, inclusive.

tasks:
  - key: my-app
    use: [build]
    app:
      endpoint: my-app-pr-${{ init.pull-request-number }}
      port: 3000
      idle-timeout: 30m
    run: pnpm start

If a user hits the URL after that, the app restarts on demand.

To print the preview link or send it somewhere (e.g. as a PR comment, a Slack message, etc), you can set up a follow-up task like this:

- key: share-link
  run: echo "$MY_APP_URL"
  env:
    MY_APP_URL: ${{ tasks.my-app.app.url }}

The above prints the canonical URL, which is pinned to the cache key of the task. Use it when you want the URL to keep pointing at the same build — for example, in integration tests that should hit a stable target. The canonical URL is shaped like:

https://{task-cache-key}--{org-slug}.rwx.run

If you'd rather the URL track the latest build for a given endpoint (so a shared PR link automatically picks up new commits), use tasks.my-app.app.url.friendly instead. The friendly URL is shaped like:

https://{endpoint}--{org-slug}.rwx.run

Backing services

Most apps need a database, cache, or other supporting services running alongside them. Use background-processes to start them up before your app boots:

tasks:
  - key: my-app
    use: [build]
    docker: true
    background-processes:
      - key: services
        run: docker compose up
    app:
      endpoint: pr-${{ init.pull-request-number }}
      port: 3000
    run: pnpm start

For apps that talk to backing services on startup, add a ready-check to avoid race conditions.

App tasks support most of the same configuration as command tasks, including env and filter.

Access

Apps can be public or private:

  • Public apps are accessible to anyone on the internet who has the URL.
  • Private apps require RWX authentication.

Apps are private by default. Set app.public to true for public access or false for private access.

Browser access to private apps

Open the app's URL as usual. If you aren't signed in to RWX, you'll be prompted to sign in before continuing to the app.

To share with someone who doesn't have an RWX account, create a share link from the app's App tab or use rwx apps share. Anyone with the link can access the app until it expires.

Programmatic access to private apps

From RWX tasks, reference the app using ${{ tasks.my-preview.app.url }} or ${{ tasks.my-preview.app.url.friendly }}. RWX handles authentication automatically using the task's default or supplied RWX_ACCESS_TOKEN.

- key: test-preview-app
  run: ./run-tests ${{ tasks.my-preview.app.url }}

For external clients, use a personal access token or a service account token for the app's organization with run:read permission. Authenticate using either of the following methods.

Token-based access

Send your token in the X-RWX-Access-Token header on each request:

curl --header "X-RWX-Access-Token: $RWX_ACCESS_TOKEN" \
  https://pr-123--myorg.rwx.run

External automation can exchange an RWX access token for a session cookie to access a private app without signing in interactively:

  1. Send a POST request to /.rwx/auth/session on the app's HTTPS URL (e.g. https://pr-123--myorg.rwx.run/.rwx/auth/session), with your token in the X-RWX-Access-Token header.
  2. RWX returns 204 No Content and sets a session cookie.
  3. Save the cookie and send it with subsequent requests to the app.

The cookie applies only to that app hostname. RWX can renew the session while the original token remains authorized. When a response includes a replacement cookie, use it for future requests.