Deploy

Preview Environments

A Preview Environment is temporary runtime capacity for your HTTP project, with its own lifecycle, storage, and billing, plus a URL when the configured exposure allows one. Use it to review a change without replacing the primary application.

HTTP projects sourced from a GitHub or Forgejo repository can create previews automatically for pull requests without a workflow, API Key, or image configured in the repository. Zenifra's GitHub Action remains available when you already build an image and want to operate the preview explicitly.

Required permissions

The minimum key for the Preview operational lifecycle is limited to the primary project:

ActionMinimum scope
Read the primary projectproject.read on project:<project-id>
List Previewsproject.preview.read on project:<project-id>
Create, update, read details and operationproject.preview.deploy on project:<project-id>
Remove a Previewproject.preview.delete on project:<project-id>

Initial Preview configuration requires an authorized user session with project.preview.configure; it is not part of the Action's minimum key. owner has full access; assistant, member, and organization API Keys need the corresponding grants.

Before you start

  1. Have an HTTP project in Zenifra.
  2. If the project source is GitHub, confirm that the GitHub account is connected to the organization and that the repository is accessible to the project. For Forgejo, confirm that the connection uses write:repository and an account that can administer repository hooks.
  3. In the console, open the primary project's Preview Environments section, choose Configure previews, and turn on Enable Preview Environments.
  4. For the native flow, turn on Enable automatic GitHub previews or Enable automatic Forgejo previews and choose the Preview target branch.
  5. Set Default expiration (hours), Maximum expiration (hours), and Active environment limit for this project. The preview uses the primary project's plan.
  6. For the Action flow, store the primary project's organization API Key in a GitHub secret and expose a ready-to-use image through a repository or organization variable.

PREVIEW=true does not enable the feature by itself. Project opt-in and the limits configured in the console are always enforced before a preview is created or updated.

Each preview is billed hourly in BRL while active. The current price and effective limits come from the catalog and project settings; check the console before enabling the feature.

How identity works

A preview's identity is made of the organization, the primary project, and the preview key. Reusing the same key reuses the same environment across runs and makes retries safe.

  • In a pull_request event, the Action uses pr-<number> when PREVIEW_KEY is not provided.
  • Outside a pull request, PREVIEW_KEY is required.
  • The key accepts 1 to 100 safe characters (A-Z, a-z, 0-9, ., _, and -). Invalid keys are rejected; they are not silently corrected.
  • pull_request events with opened, synchronize, or reopened upsert the preview.
  • pull_request.closed requests immediate removal.
  • Reopening a pull request with the same key recreates or reactivates the same logical identity and keeps the URL whenever possible.

A successful create or update renews the expiration. Duplicate deliveries and idempotent retries do not extend the TTL merely by being repeated. The default TTL is 24 hours and can be configured from 1 hour to 168 hours. If the close workflow does not run, Zenifra removes expired previews automatically.

Native previews for GitHub and Forgejo pull requests

The native flow uses the repository and commands already configured for the GitHub or Forgejo project. After Preview Environments and the provider's automatic previews are enabled, choose a target branch such as main. This branch is independent from the primary update mode: it defines which pull requests create previews and does not change the branch used to update the primary application.

For pull requests opened in the project's own repository:

  • opened, reopened, and synchronize create or update the preview identified by pr-<number> when the target branch matches the selected branch;
  • edited reevaluates the pull request when its target branch changes, creating, updating, or removing the preview according to the new eligibility;
  • closed removes the corresponding preview;
  • pull requests targeting another branch do not create a preview;
  • pull requests from forks do not participate in the native flow;
  • pull requests that were already open before activation are not imported automatically; they are considered on the next relevant event.

On Forgejo, events arrive through the repository hook created by the connection; without permission to administer hooks, automatic previews cannot be enabled.

Each pull request keeps the same preview identity across updates. Zenifra uses the runtime and commands configured for the project to build the pull request commit. The primary project keeps its own version while the preview is created or updated.

The preview follows the primary project's plan, exposure, and variables according to the current Preview behavior. Inherited variables may point to the same services used by the primary application. Storage is new and starts empty for the preview; data, custom domains, and custom image commands are not inherited. Do not treat the preview as a copy of production data.

This path does not require a GitHub Action, API Key, IMAGE, PREVIEW_KEY, or workflow configuration in the repository. To build an image outside this flow or operate a preview with your own automation, use the GitHub Action described below.

Warning: use only one flow for the pr-<number> key in the same project. If an Action-managed preview already uses that key, the native flow does not replace it and the native run is blocked.

If you disable the provider's automatic previews or Preview Environments, change the target branch, or disconnect/change the project's repository, affected native previews are removed and pending publications are invalidated. The primary application and its update mode remain separate from this cleanup.

GitHub App permission

To receive pull request events, the Zenifra GitHub App needs the Pull requests permission (read-only). Installations made before this permission keep working for deploys, but do not create previews until the update is approved on GitHub:

  1. Someone with admin access to the account or organization opens its settings on GitHub, under GitHub Apps (for personal accounts, Applications > Installed GitHub Apps).
  2. Next to Zenifra, choose Configure.
  3. In the permission update notice, choose Review request and then Accept new permissions.

After the approval, the next pull request event, such as a new push, creates or updates the preview.

Preview inheritance and behavior

Every Preview inherits user-configured environment variables from the primary project inside the platform on every create or update. Values are never exposed by the API, Console, or Action.

Variable values never pass through the Action, outputs, Job Summary, or logs. Zenifra-managed variables are regenerated for the preview instead of being treated as user variables from the primary project.

Warning: inherited variables may point to the same databases, queues, buckets, or other services used by the primary project. Review these variables before using the preview when that sharing is not intended.

The preview inherits the port, exposure, and access rules needed to run the application. Storage is new and starts empty for the preview; data from the primary project is not inherited.

These configurations are currently not inherited or created:

  • database, queue, bucket, and other service data
  • custom domains
  • custom image startup commands

Preview environment limits per organization

Active environments are limited at two levels: organization and primary project. The effective limit for a preview is always the lowest of the two.

  • Per organization: an organization can have at most 10 simultaneously active preview environments.
  • Per project: a primary project can have at most 2 simultaneously active preview environments.
  • Maximum TTL: a preview can expire up to 168 hours (7 days) after the last update.

Limits are counted while the preview exists, regardless of the plan: each active preview consumes one slot in both limits. Creating a preview beyond the effective limit makes the operation end as failed, with a public message stating that the organization or project limit was reached; the Action fails the job.

The project opt-in, the limits configured in the console, and the organization entitlement are always enforced before any create, update, or removal. The public API does not change the organization limit: project settings can be adjusted in the console, but the organization limit is set by Zenifra.

How to increase the organization limit

If your organization needs more concurrent preview environments, send an email to [email protected] including:

  1. the organization name and access email;
  2. the current usage (how many active previews the organization typically keeps);
  3. the desired limit and the reason (for example, multiple development fronts working on parallel pull requests).

The Zenifra team reviews the request, adjusts the organization limit, and confirms by email. After approval, the new limits apply immediately to subsequent upserts, with no workflow changes required.

GitHub Action inputs

This section describes the explicit Action flow. It is independent from native pull request previews and remains valid for projects with a ready-to-use image, including projects that do not use a GitHub source.

Use the names exactly as shown below. Existing inputs continue to work when PREVIEW is absent or false; in that case, the Action updates only the primary project.

InputRequiredDefaultDescription
PROJECT_IDYes—ID of the primary HTTP project.
API_KEYYes—Primary project organization API Key, stored in a GitHub secret. It is never displayed.
IMAGEUpsert—Ready-to-use image for deployment. Required for create/update and may be omitted when removing.
PREVIEWNofalseSet to true to select Preview Environment mode.
PREVIEW_KEYConditionalAutomatic in PRStable preview key. In pull requests, the Action derives pr-<number>; elsewhere, provide a valid key.
PREVIEW_TTLNo24hLifetime until expiration. Accepts 1h to 168h.
PREVIEW_ACTIONNoautoauto, upsert, or delete. With auto, a closed PR selects delete and all other cases select upsert.
WAIT_TIMEOUTNo10mMaximum time to wait for the operation, from 1s to 15m.

Every Preview inherits the primary project's user variables on every upsert without exposing their values.

The Preview Environment always inherits the primary project's plan. If the project uses basic, the preview uses basic; there is no PREVIEW_PLAN or separate Preview price.

The Action validates booleans, duration, action, and context before making the request. IMAGE is conditional: it is not required for deletion. Invalid values, a disallowed plan, and an out-of-range TTL fail the job with a public, actionable message.

This workflow uses one key derived from the pull request number. Updates to the same pull request reuse the preview, and the closed event removes it through PREVIEW_ACTION=auto.

name: Zenifra preview

on:
  pull_request:
    types: [opened, synchronize, reopened, closed]

permissions:
  contents: read

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - name: Create or remove preview
        id: zenifra-preview
        uses: zenifra/[email protected]
        with:
          PROJECT_ID: ${{ vars.ZENIFRA_PROJECT_ID }}
          API_KEY: ${{ secrets.ZENIFRA_API_KEY }}
          IMAGE: ${{ vars.ZENIFRA_PREVIEW_IMAGE }}
          PREVIEW: true
          PREVIEW_ACTION: auto
          PREVIEW_TTL: 24h
          WAIT_TIMEOUT: 10m

On the closed event, the Action does not need the image to remove the environment. Upsert outputs become available only after the operation reaches available; if the operation fails, the job ends with an error.

Manual workflow with PREVIEW_KEY

Use workflow_dispatch to test a branch, a specific version, or a flow that is not triggered by a pull request. In this case, you must choose the key yourself.

name: Zenifra manual preview

on:
  workflow_dispatch:
    inputs:
      preview_key:
        description: Stable Preview Environment key
        required: true
        type: string
      action:
        description: Requested operation
        required: true
        default: upsert
        type: choice
        options:
          - upsert
          - delete

permissions:
  contents: read

jobs:
  preview:
    runs-on: ubuntu-latest
    steps:
      - name: Run preview operation
        id: zenifra-preview
        uses: zenifra/[email protected]
        with:
          PROJECT_ID: ${{ vars.ZENIFRA_PROJECT_ID }}
          API_KEY: ${{ secrets.ZENIFRA_API_KEY }}
          IMAGE: ${{ vars.ZENIFRA_PREVIEW_IMAGE }}
          PREVIEW: true
          PREVIEW_KEY: ${{ inputs.preview_key }}
          PREVIEW_ACTION: ${{ inputs.action }}
          PREVIEW_TTL: 24h
          WAIT_TIMEOUT: 10m

When delete is selected, IMAGE may be omitted. Do not reuse the same key for changes that must exist at the same time: a key represents one logical environment.

Outputs, polling, and status

The Action follows the operation with bounded polling. It waits for available on an upsert and deleted on removal. Transitional states include accepted, reserving, provisioning, updating, and deleting; failed ends the job with an error.

When the expected state is reached, the Action provides:

OutputDescription
preview_idPublic identifier for the Preview Environment.
preview_urlPublic preview URL when it is available. It may not exist during removal.
expires_atCurrent expiration date and time.
operation_idPublic identifier for the tracked operation.
preview_statusFinal state: available for an upsert or deleted for a removal.

The Job Summary contains the public operation result, but never API Keys, environment variables, or credentials. A wait timeout or incompatible terminal state fails the job; repeating the same operation with the same key is safe.

Public errors and troubleshooting

Messages are product-oriented and do not reveal operational details. The API returns a code field that helps you correct the workflow; the Action shows an equivalent public message in the job:

  • preview_disabled: enable Preview Environments for the primary project.
  • preview_ttl_invalid or preview_ttl_exceeds_limit: use a TTL between 1h and 168h that respects the maximum expiration configured for the project.
  • PREVIEW_OPERATION_CONFLICT: another operation is already in progress for the key; wait for it or follow the existing operation instead of creating a new key to bypass the state.
  • PARENT_CONFIGURATION_UNAVAILABLE: check the primary project's configuration before trying again.
  • PARENT_IMAGE_AUTHENTICATION_UNAVAILABLE: review the primary project's private image credentials.
  • Operation failed because a limit was reached: wait for an environment to be removed or request a higher limit.
  • Key outside the allowed format: the Action rejects the run before calling the API.
  • Timed out waiting for the preview operation to finish.: increase WAIT_TIMEOUT up to 15m or query the operation again.

No public error includes resource names, topology, credentials, variables, private URLs, or cleanup details.

Next steps

Last updated on

On this page