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
endpointwill be part of the app's subdomain, and must be alphanumeric or hyphens (no underscores). - The
portis what will bind to your server process, and must be between 1024 and 49151.
By default, the port is also exposed as environment variable named PORT.
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.
Sharing the preview link
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
Cookie-based access
External automation can exchange an RWX access token for a session cookie to access a private app without signing in interactively:
- Send a
POSTrequest to/.rwx/auth/sessionon the app's HTTPS URL (e.g.https://pr-123--myorg.rwx.run/.rwx/auth/session), with your token in theX-RWX-Access-Tokenheader. - RWX returns
204 No Contentand sets a session cookie. - 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.