> ## Documentation Index
> Fetch the complete documentation index at: https://docs.membrane.agency/llms.txt
> Use this file to discover all available pages before exploring further.

# Self Hosting

Our product is distributed as a set of Docker images that can be deployed in your infrastructure.

## Docker Registry Access

Contact our support team to receive access credentials to our Docker registry (`harbor.getmembrane.com`).
You'll receive a username in the format `robot$<your-company-name>` and a password.

```bash theme={null}
docker login harbor.getmembrane.com
```

### Image Versioning

Images are tagged with `:latest` and date-based immutable tags (e.g., `2026-01-14`).
For production deployments, we recommend using the immutable tags: `harbor.getmembrane.com/core/api:2026-01-14`.

## Infrastructure Requirements

Membrane requires:

* Cloud storage (AWS S3, Azure Blob Storage, or Google Cloud Storage)
* MongoDB server
* Redis server
* Authentication provider: Auth0 (free tier is sufficient) or BetterAuth

## Authentication

Membrane self-hosting supports two authentication providers:

* **Auth0**
* **BetterAuth**

Use the same provider in both API and Console services.

### Choose Your Setup

* **Auth0**: use Auth0-hosted authentication for users.
* **BetterAuth + Resend**: use BetterAuth with Resend-managed email delivery.
* **BetterAuth + Webhook**: use BetterAuth with your own email delivery backend.
* **BetterAuth + OIDC**: use BetterAuth with enterprise SSO via OpenID Connect.

### Auth0

#### Auth0 Application Setup

When configuring your Auth0 application:

* Application Type: Single-page Application
* Allowed Callback URLs: Base URL of your console service
* Allowed Web Origins: Base URL of your console service

#### API Pods Environment Variables (Auth0)

Configure these variables on all API pods.

| Variable              | Required | Description                        |
| :-------------------- | :------- | :--------------------------------- |
| `AUTH_PROVIDER`       | Yes      | Set to `auth0` to force Auth0 mode |
| `AUTH0_DOMAIN`        | Yes      | Auth0 tenant domain                |
| `AUTH0_CLIENT_ID`     | Yes      | Auth0 application client ID        |
| `AUTH0_CLIENT_SECRET` | Yes      | Auth0 application secret           |

#### Console Pods Environment Variables (Auth0)

| Variable                      | Required | Description                           |
| :---------------------------- | :------- | :------------------------------------ |
| `NEXT_PUBLIC_AUTH_PROVIDER`   | Yes      | Set to `auth0`                        |
| `NEXT_PUBLIC_AUTH0_DOMAIN`    | Yes      | Auth0 tenant domain for console login |
| `NEXT_PUBLIC_AUTH0_CLIENT_ID` | Yes      | Auth0 client ID for console login     |

### BetterAuth

#### API Pods Environment Variables (BetterAuth)

Configure these variables on all API pods.

| Variable                    | Required    | Description                                                                   |
| :-------------------------- | :---------- | :---------------------------------------------------------------------------- |
| `AUTH_PROVIDER`             | Yes         | Set to `betterauth` to force BetterAuth mode                                  |
| `AUTH_SECRET`               | Yes         | BetterAuth session signing secret                                             |
| `CONSOLE_BASE_URI`          | Yes         | Console URL used for auth redirects and verification flows                    |
| `BETTERAUTH_EMAIL_PROVIDER` | Conditional | Required unless `AUTH_OIDC_ENABLED=true`; allowed values: `webhook`, `resend` |

#### BetterAuth Email Providers

Choose one email provider for BetterAuth email delivery.

##### Resend (API pod environment variables)

Set these variables on all API pods:

| Variable                    | Required | Description                                    |
| :-------------------------- | :------- | :--------------------------------------------- |
| `BETTERAUTH_EMAIL_PROVIDER` | Yes      | Set to `resend`                                |
| `RESEND_API_KEY`            | Yes      | Resend API key                                 |
| `BETTERAUTH_EMAIL_FROM`     | Yes      | Sender email address used by BetterAuth emails |

##### Webhook (database-backed configuration)

Set this variable on all API pods:

| Variable                    | Required | Description      |
| :-------------------------- | :------- | :--------------- |
| `BETTERAUTH_EMAIL_PROVIDER` | Yes      | Set to `webhook` |

###### Setup Database Documents

When using `webhook`, create both webhook types in the `webhooks` collection:

* `better-auth-signup-verify` (for email verification messages)
* `better-auth-reset-password` (for password reset messages)

```javascript theme={null}
// Open your DB CLI
// Select database
use <db name>

// Insert BetterAuth webhook docs
db["webhooks"].insertMany([
  {
    type: "better-auth-signup-verify",
    url: "<webhook_url_signup_verify>",
    secret: "<webhook_secret>",
    createdAt: new Date(),
    updatedAt: new Date()
  },
  {
    type: "better-auth-reset-password",
    url: "<webhook_url_reset_password>",
    secret: "<webhook_secret>",
    createdAt: new Date(),
    updatedAt: new Date()
  }
])
```

###### Document Fields

Values to include in each webhook document:

* `type`: one of `better-auth-signup-verify` or `better-auth-reset-password`.
* `url`: your endpoint that sends the email.
* `secret`: optional but strongly recommended; if set, Membrane sends `X-Signature` (HMAC-SHA256).
* `createdAt` and `updatedAt`: timestamps for record management.

See [System Webhooks](/docs/managing-membrane/self-hosting/system-webhooks) for signature verification and webhook handler guidance.

#### Social Logins

##### API pod environment variables (Social Logins)

| Variable                       | Required    | Description                                       |
| :----------------------------- | :---------- | :------------------------------------------------ |
| `AUTH_GOOGLE_CLIENT_ID`        | Conditional | Required to enable authentication using Google    |
| `AUTH_GOOGLE_CLIENT_SECRET`    | Conditional | Required to enable authentication using Google    |
| `AUTH_GITHUB_CLIENT_ID`        | Conditional | Required to enable authentication using GitHub    |
| `AUTH_GITHUB_CLIENT_SECRET`    | Conditional | Required to enable authentication using GitHub    |
| `AUTH_MICROSOFT_CLIENT_ID`     | Conditional | Required to enable authentication using Microsoft |
| `AUTH_MICROSOFT_CLIENT_SECRET` | Conditional | Required to enable authentication using Microsoft |

##### Console pod environment variables (Social Logins)

| Variable                                  | Required    | Description                                                             |
| :---------------------------------------- | :---------- | :---------------------------------------------------------------------- |
| `NEXT_PUBLIC_BETTERAUTH_SOCIAL_PROVIDERS` | Conditional | Comma-separated providers shown in login UI (`google,github,microsoft`) |

#### OpenID Connect (OIDC)

Use either discovery mode (`AUTH_OIDC_DISCOVERY_URL`) or manual endpoint mode (`AUTH_OIDC_AUTHORIZATION_URL` + `AUTH_OIDC_TOKEN_URL`).

##### API pod environment variables (OIDC)

| Variable                              | Required    | Description                                                          |
| :------------------------------------ | :---------- | :------------------------------------------------------------------- |
| `AUTH_OIDC_ENABLED`                   | Yes         | Set to `true` to enable custom OIDC                                  |
| `AUTH_OIDC_CLIENT_ID`                 | Yes         | OIDC client ID                                                       |
| `AUTH_OIDC_CLIENT_SECRET`             | Yes         | OIDC client secret                                                   |
| `AUTH_OIDC_DISCOVERY_URL`             | Conditional | Use this for discovery mode                                          |
| `AUTH_OIDC_AUTHORIZATION_URL`         | Conditional | Required with `AUTH_OIDC_TOKEN_URL` when not using discovery         |
| `AUTH_OIDC_TOKEN_URL`                 | Conditional | Required with `AUTH_OIDC_AUTHORIZATION_URL` when not using discovery |
| `AUTH_OIDC_PROVIDER_ID`               | No          | Provider id, default `oidc`                                          |
| `AUTH_OIDC_SCOPES`                    | No          | Comma-separated scopes, default `openid,email,profile`               |
| `AUTH_OIDC_PKCE`                      | No          | Enable PKCE for authorization code flow                              |
| `AUTH_OIDC_REQUIRE_ISSUER_VALIDATION` | No          | Enforce strict issuer validation                                     |

##### Console pod environment variables (OIDC)

| Variable                                   | Required    | Description                                              |
| :----------------------------------------- | :---------- | :------------------------------------------------------- |
| `NEXT_PUBLIC_BETTERAUTH_OIDC_ENABLED`      | Conditional | Set to `true` to show OIDC login button                  |
| `NEXT_PUBLIC_BETTERAUTH_OIDC_PROVIDER_ID`  | No          | Must match API `AUTH_OIDC_PROVIDER_ID` (default: `oidc`) |
| `NEXT_PUBLIC_BETTERAUTH_OIDC_BUTTON_LABEL` | No          | Custom login button label                                |

## Core Services

Membrane consists of four essential services:

### API Service

The primary engine API that stores and executes integrations.

**Docker Image:** `harbor.getmembrane.com/core/api`

The API service operates in four distinct modes, each activated by specific environment variables:

<br />

#### 1. API Mode

Main backend service handling incoming traffic and processing HTTP requests.

**Mode-specific Environment Variables:**

| Variable               | Example | Description                                 |
| :--------------------- | :------ | :------------------------------------------ |
| `IS_API`               | 1       | Enables API mode                            |
| `HEADERS_TIMEOUT_MS`   | 61000   | Maximum time to receive request headers     |
| `KEEPALIVE_TIMEOUT_MS` | 61000   | Maximum time to keep idle connections alive |

<br />

#### 2. Instant Tasks Worker Mode

Designed for executing semi-instant asynchronous tasks. This mode should be scaled to prevent task queuing. Each worker processes one background job at a time.

**Mode-specific Environment Variables:**

| Variable                  | Example | Description                       |
| :------------------------ | :------ | :-------------------------------- |
| `IS_INSTANT_TASKS_WORKER` | 1       | Enables instant tasks worker mode |

<br />

#### 3. Queued Tasks Worker Mode

Handles long-running tasks such as flow runs, event pulls, and external events. Each worker processes one background job at a time. When limits are enabled, tasks are queued and executed to ensure fair resource distribution among tenants.

**Mode-specific Environment Variables:**

| Variable                                | Example | Description                                  |
| :-------------------------------------- | :------ | :------------------------------------------- |
| `IS_QUEUED_TASKS_WORKER`                | 1       | Enables queued tasks worker mode             |
| `MAX_QUEUED_TASKS_MEMORY_MB`            | 1024    | Memory limit for task runs (default: `1024`) |
| `MAX_QUEUED_TASKS_PROCESS_TIME_SECONDS` | 3000    | Time limit for task runs (default: `3000`)   |

<br />

#### 4. Orchestrator Mode

Manages schedule triggers, handles Data Sync schedules, and performs cleanup tasks.

**Mode-specific Environment Variables:**

| Variable          | Example | Description               |
| :---------------- | :------ | :------------------------ |
| `IS_ORCHESTRATOR` | 1       | Enables orchestrator mode |

All services scale horizontally without additional configuration (aside from load balancing for API services).

<br />

**Common Environment Variables**

The following variables are required for all operation modes:

<table>
  <thead>
    <tr>
      <th>
        Variable
      </th>

      <th>
        Description
      </th>

      <th>
        Example
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `NODE_ENV`
      </td>

      <td>
        Environment mode
      </td>

      <td>
        production
      </td>
    </tr>

    <tr>
      <td>
        `BASE_URI`
      </td>

      <td>
        Service deployment URL
      </td>

      <td>
        [https://api.yourdomain.com](https://api.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        `BASE_WEBHOOKS_URI`
      </td>

      <td>
        Optional: Override webhook endpoint base URL. Use this when you need webhooks to be accessible at a different URL than the main API (e.g., when the API is private but webhooks need a public proxy). Works the same way as BASE\_URI, but only for webhook endpoints.
      </td>

      <td>
        [https://webhooks-proxy.yourdomain.com](https://webhooks-proxy.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        `BASE_OAUTH_CALLBACK_URI`
      </td>

      <td>
        Optional: Override OAuth callback endpoint base URL. Use this when you need OAuth callbacks to be accessible at a different URL than the main API (e.g., when the API is private but OAuth callbacks need a public proxy). Works the same way as BASE\_URI, but only for OAuth callback endpoints.
      </td>

      <td>
        [https://oauth-proxy.yourdomain.com](https://oauth-proxy.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        `BASE_URI_INTERNAL`
      </td>

      <td>
        Internal service deployment URI (to be used for call from workers to the service)
      </td>

      <td>
        [https://api.yourdomain.com](https://api.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        `CODE_SANDBOX_URI`
      </td>

      <td>
        Code Sandbox service URL
      </td>

      <td>
        [https://code-sandbox.yourdomain.com](https://code-sandbox.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        Authentication variables
      </td>

      <td>
        Provider-specific API authentication variables. See <a href="#authentication">Authentication</a> section.
      </td>

      <td>
        `AUTH_PROVIDER`, `AUTH0_*`, `AUTH_SECRET`, `BETTERAUTH_*`
      </td>
    </tr>

    <tr>
      <td>
        `TMP_STORAGE_BUCKET`
      </td>

      <td>
        Temporary storage bucket (recommend auto-expiration policy)
      </td>

      <td>
        integration-app-tmp
      </td>
    </tr>

    <tr>
      <td>
        `CONNECTORS_STORAGE_BUCKET`
      </td>

      <td>
        Connectors storage bucket
      </td>

      <td>
        integration-app-connectors
      </td>
    </tr>

    <tr>
      <td>
        `STATIC_STORAGE_BUCKET`
      </td>

      <td>
        Static files storage bucket
      </td>

      <td>
        integration-app-static
      </td>
    </tr>

    <tr>
      <td>
        `BASE_STATIC_URI`
      </td>

      <td>
        Static content base URL (files uploaded to the static bucket should be served from this URL)
      </td>

      <td>
        [https://static.yourdomain.com](https://static.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        `REDIS_URI` or `REDIS_CLUSTER_URI_X`
      </td>

      <td>
        URL for Redis server. For Redis cluster, provide multiple: REDIS\_CLUSTER\_URI\_1, REDIS\_CLUSTER\_URI\_2, ...
      </td>

      <td>
        redis\://user:[password@redis-service.com](mailto:password@redis-service.com):6379/
      </td>
    </tr>

    <tr>
      <td>
        `WORKER_REDIS_URI` or `WORKER_REDIS_CLUSTER_URI_X`
      </td>

      <td>
        Optional. URL for a dedicated Redis server for background worker tasks.
        This allows you to separate worker task runs from main Redis operations for better performance and isolation.
        If not provided, the main Redis server will be used for both purposes.

        For Redis clusters, provide multiple:
        WORKER\_REDIS\_CLUSTER\_URI\_1, WORKER\_REDIS\_CLUSTER\_URI\_2, etc.
      </td>

      <td>
        redis\://user:[password@redis-service.com](mailto:password@redis-service.com):6379/
      </td>
    </tr>

    <tr>
      <td>
        `SECRET`
      </td>

      <td>
        JWT token signing secret
      </td>

      <td>
        s3cr3tString
      </td>
    </tr>

    <tr>
      <td>
        `ENCRYPTION_SECRET`
      </td>

      <td>
        Credentials encryption secret
      </td>

      <td>
        v3rys3cr3tstring
      </td>
    </tr>

    <tr>
      <td>
        `MONGO_URI`
      </td>

      <td>
        MongoDB connection string
      </td>

      <td>
        mongodb+srv://login:[pass@mymongo.com](mailto:pass@mymongo.com)/integration-api
      </td>
    </tr>

    <tr>
      <td>
        `PORT`
      </td>

      <td>
        Container listening port (default: 5000)
      </td>

      <td>
        5000
      </td>
    </tr>

    <tr>
      <td>
        `AWS_REGION`
      </td>

      <td>
        AWS S3 region (when using S3 storage)
      </td>

      <td>
        eu-central-1
      </td>
    </tr>

    <tr>
      <td>
        `AWS_ACCESS_KEY_ID`
      </td>

      <td>
        AWS S3 access key (when using S3 storage)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `AWS_SECRET_ACCESS_KEY`
      </td>

      <td>
        AWS S3 secret key (when using S3 storage)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `STORAGE_PROVIDER`
      </td>

      <td>
        Storage provider type: `s3` (AWS S3), `abs` (Azure Blob Storage), or `gcs` (Google Cloud Storage). Default: `s3`
      </td>

      <td>
        abs
      </td>
    </tr>

    <tr>
      <td>
        `AZURE_STORAGE_CONNECTION_STRING`
      </td>

      <td>
        Azure Blob Storage connection string (when using Azure storage)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `AZURE_STORAGE_ACCOUNT_NAME`
      </td>

      <td>
        Azure storage account name (alternative to connection string)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `AZURE_STORAGE_ACCOUNT_KEY`
      </td>

      <td>
        Azure storage account key (alternative to connection string)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `GOOGLE_CLOUD_PROJECT_ID`
      </td>

      <td>
        Google Cloud project ID (when using GCS storage)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `GOOGLE_CLOUD_KEYFILE`
      </td>

      <td>
        Path to Google Cloud service account key file (optional, uses Application Default Credentials if not provided)
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `ENABLE_LIMITS`
      </td>

      <td>
        Optional: Enable workspace resource limits
      </td>

      <td>
        true
      </td>
    </tr>

    <tr>
      <td>
        `CONNECTION_CREDENTIALS_STORAGE_TYPE`
      </td>

      <td>
        Optional: storage type for connection credentials: `database`, `external_api`. Default: `database`
      </td>

      <td />
    </tr>

    <tr>
      <td>
        `CONNECTION_CREDENTIALS_EXTERNAL_API_ENDPOINT_URL`
      </td>

      <td>
        When `external_api` chosen for connection credentials storage, this is required.
      </td>

      <td>
        [https://api.yourdomain.com](https://api.yourdomain.com)
      </td>
    </tr>

    <tr>
      <td>
        `MAX_NODE_RUN_OUTPUT_SIZE_MB`
      </td>

      <td>
        Optional: Maximum size in megabytes for node run outputs. Default: `20`
      </td>

      <td>
        50
      </td>
    </tr>

    <tr>
      <td>
        `MONGO_MAX_IDLE_TIME_MS`
      </td>

      <td>
        Maximum time that a connection can remain idle in the pool before being closed. Default: 0 (connections never timeout)
      </td>

      <td>
        300000
      </td>
    </tr>

    <tr>
      <td>
        `MONGO_MIN_POOL_SIZE`
      </td>

      <td>
        Minimum number of connections to maintain in the MongoDB connection pool. Default: 0 (no minimum)
      </td>

      <td>
        3
      </td>
    </tr>

    <tr>
      <td>
        `MONGO_MAX_POOL_SIZE`
      </td>

      <td>
        Maximum number of connections allowed in the MongoDB connection pool. Default: 100
      </td>

      <td>
        100
      </td>
    </tr>

    <tr>
      <td>
        `MONGO_SERVER_SELECTION_TIMEOUT_MS`
      </td>

      <td>
        Maximum time in milliseconds to wait for MongoDB server selection. Useful in containerized environments with slower DNS resolution. Default: 30000 (MongoDB default)
      </td>

      <td>
        60000
      </td>
    </tr>

    <tr>
      <td>
        `REDIS_CONNECT_TIMEOUT_MS`
      </td>

      <td>
        Maximum time in milliseconds to wait for Redis connection. Default: 10000 (standalone), 60000 (cluster)
      </td>

      <td>
        60000
      </td>
    </tr>

    <tr>
      <td>
        `REDIS_DISABLE_TLS_VERIFICATION`
      </td>

      <td>
        Set to `true` to disable TLS certificate verification for Redis connections. Required for AWS ElastiCache with in-transit encryption.
      </td>

      <td>
        true
      </td>
    </tr>

    <tr>
      <td>
        `SKIP_HEALTH_CHECKS`
      </td>

      <td>
        Skip specific health checks during startup. Use `all` to skip all checks, or a comma-separated list of: `mongo`, `redis`, `storage`, `custom_code`. Useful for Kubernetes environments with slower initial DNS resolution.
      </td>

      <td>
        all
      </td>
    </tr>

    <tr>
      <td>
        `HEALTH_CHECK_RETRIES`
      </td>

      <td>
        Number of retry attempts for health checks after initial failure. Health checks use exponential backoff with jitter between retries. Default: 3
      </td>

      <td>
        5
      </td>
    </tr>

    <tr>
      <td>
        `HEALTH_CHECK_RETRY_DELAY_MS`
      </td>

      <td>
        Initial delay between health check retries in milliseconds. Subsequent retries use exponential backoff (delay doubles each retry). Default: 1000
      </td>

      <td>
        2000
      </td>
    </tr>

    <tr>
      <td>
        `HEALTH_CHECK_MAX_RETRY_DELAY_MS`
      </td>

      <td>
        Maximum delay between health check retries in milliseconds. Prevents exponential backoff from growing too large. Default: 10000
      </td>

      <td>
        30000
      </td>
    </tr>

    <tr>
      <td>
        `SELF_HOSTING_TOKEN`
      </td>

      <td>
        **Required for self-hosted deployments.** Token for validating your self-hosted Membrane installation. Generate this token from your <a href="https://console.getmembrane.com/w/0/settings/organization/self-hosting-tokens" target="_blank">organization settings</a> in console. The token is validated against Membrane's cloud API on startup.
      </td>

      <td>
        sht\_abc123...
      </td>
    </tr>
  </tbody>
</table>

### UI Service

Provides pre-built integration user interfaces.

**Docker Image:** `harbor.getmembrane.com/core/ui`

**Environment Variables:**

| Variable                 | Description                                       | Example                                                  |
| ------------------------ | ------------------------------------------------- | -------------------------------------------------------- |
| `NEXT_PUBLIC_ENGINE_URI` | API service URL                                   | [https://api.yourdomain.com](https://api.yourdomain.com) |
| `BASE_PATH`              | Optional: URL path prefix for subpath deployments | /ui-app                                                  |
| `PORT`                   | Container listening port                          | 5000                                                     |

### Console Service

Administration interface for managing integrations.

**Docker Image:** `harbor.getmembrane.com/core/console`

**Environment Variables:**

| Variable                     | Description                                                                                        | Example                                                                                  |
| ---------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `NEXT_PUBLIC_BASE_URI`       | Console access URL                                                                                 | [https://console.integration.yourdomain.com](https://console.integration.yourdomain.com) |
| `NEXT_PUBLIC_ENGINE_API_URI` | API service URL                                                                                    | [https://api.integrations.yourdomain.com](https://api.integrations.yourdomain.com)       |
| `NEXT_PUBLIC_ENGINE_UI_URI`  | UI service URL                                                                                     | [https://ui.integrations.yourdomain.com](https://ui.integrations.yourdomain.com)         |
| Authentication variables     | Provider-specific console authentication variables. See [Authentication](#authentication) section. | `NEXT_PUBLIC_AUTH_PROVIDER`, `NEXT_PUBLIC_AUTH0_*`, `NEXT_PUBLIC_BETTERAUTH_*`           |
| `BASE_PATH`                  | Optional: URL path prefix for subpath deployments                                                  | /console-app                                                                             |
| `PORT`                       | Container listening port                                                                           | 5000                                                                                     |
| `NEXT_PUBLIC_ENABLE_LIMITS`  | Optional: Enable limits management UI                                                              | true                                                                                     |

#### Subpath Deployments

The `BASE_PATH` environment variable enables deployment on a URL subpath (e.g., `integrations.yourdomain.com/ui-app` or `integrations.yourdomain.com/console-app`) rather than requiring a dedicated domain or subdomain. When set, it automatically adjusts all asset paths to work correctly under the specified path prefix.

**Example:** Setting `BASE_PATH=/console-app` allows the Console service to be accessed at `https://integrations.yourdomain.com/console-app` with all resources loading from the correct paths.

### Custom Code Runner

Provides an isolated environment for executing custom code in connectors or middleware. This service should only be accessible internally from other services.

**Docker Image:** `harbor.getmembrane.com/core/custom-code-runner`

> **Note**: On AMD architecture (not ARM), set `CUSTOM_CODE_MEMORY_LIMIT` environment variable for the API service to at least 21474836480 (20GB) to ensure sufficient virtual memory for WebAssembly. Physical memory allocation of 2GB is typically sufficient.

## Scaling Recommendations

Backend services emit custom metrics that help determine scaling conditions. The following table outlines these metrics:

| Metric                                    | Emitted by             | Endpoint                    | Description                                                                                           |
| :---------------------------------------- | :--------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------- |
| `instant_tasks_worker_utilization_rate`   | `instant-tasks-worker` | `/prometheus/instant-tasks` | Value (0.0-1.0) representing the percentage of time instant tasks worker is actively processing tasks |
| `custom_code_runner_total_job_spaces`     | `custom-code-runner`   | `/api/v2/prometheus`        | Total number of job spaces supported by a custom-code-runner pod                                      |
| `custom_code_runner_remaining_job_spaces` | `custom-code-runner`   | `/api/v2/prometheus`        | Available number of job spaces in a custom-code-runner pod                                            |
| `queued_tasks_worker_utilization_rate`    | `queued-tasks-worker`  | `/prometheus/queued-tasks`  | Value (0.0-1.0) representing the percentage of time queued tasks worker is actively processing tasks. |

All services scale horizontally. The following table outlines the scaling recommendations for each service for production workloads:

<table>
  <thead>
    <tr>
      <th>
        Container
      </th>

      <th>
        Scaling Approach
      </th>

      <th>
        Recommended Values
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        Console
      </td>

      <td>
        Fixed number of instances to ensure availability and zero-downtime updates.
      </td>

      <td>
        Instances: 2
      </td>
    </tr>

    <tr>
      <td>
        UI
      </td>

      <td>
        Fixed number of instances to ensure availability and zero-downtime updates.
      </td>

      <td>
        Instances: 2
      </td>
    </tr>

    <tr>
      <td>
        Orchestrator
      </td>

      <td>
        Fixed number of instances to ensure availability and zero-downtime updates.
      </td>

      <td>
        Instances: 2
      </td>
    </tr>

    <tr>
      <td>
        API
      </td>

      <td>
        Dynamic scaling based on resource usage. Monitor memory and CPU usage to determine scaling needs.
      </td>

      <td>
        Threshold: 50%
        Min instances: 2
      </td>
    </tr>

    <tr>
      <td>
        Instant Tasks Worker
      </td>

      <td>
        Dynamic scaling based on the number of active and waiting tasks, using a modifier to adjust the scaling sensitivity.
        Pseudocode:`avg(instant_tasks_worker_utilization_rate)`
      </td>

      <td>
        Threshold: 0.75
        Min instances: 2
      </td>
    </tr>

    <tr>
      <td>
        Queued Tasks Worker
      </td>

      <td>
        Dynamic scaling based on workers utilization rate.
        Pseudocode:`avg(queued_tasks_worker_utilization_rate)`
      </td>

      <td>
        Threshold: 0.85
        Min instances: 2
      </td>
    </tr>

    <tr>
      <td>
        Custom Code Runner
      </td>

      <td>
        Dynamic scaling based on job spaces availability. Monitor the number of total and remaining job spaces and auto scale maintain a certain rate.

        Pseudocode:
        `(custom_code_runner_total_job_spaces -custom_code_runner_remaining_job_spaces
                                                                                                                                                                                                                        ) / custom_code_runner_total_job_spaces`
      </td>

      <td>
        Threshold: 0.45
        Min instances: 2
      </td>
    </tr>
  </tbody>
</table>

<br />

## Connector Management

### Automated Deployment

Use [Membrane CLI](https://www.npmjs.com/package/@membranehq/membrane-cli) to migrate connectors from cloud to self-hosted environments.

### Manual Deployment

Upload connector .zip archives through the Console UI via Integrations > Apps > Upload Connector.

## Troubleshooting

For enhanced debugging output, add `DEBUG_ALL=1` to any container's environment variables.

## Helm

<Columns cols={2}>
  <Card title="Helm" href="/docs/managing-membrane/self-hosting/helm" target="_blank">
    Guide for installing Membrane using Helm chart.
  </Card>
</Columns>

## Cloud-specific Guides

<Columns cols={3}>
  <Card title="AWS Self-Hosting" href="/docs/managing-membrane/self-hosting/aws" target="_blank">
    Guide for deploying Membrane on AWS.
  </Card>

  <Card title="Azure Self-Hosting" href="/docs/managing-membrane/self-hosting/azure" target="_blank">
    Guide for deploying Membrane on Azure.
  </Card>

  <Card title="GCP Self-Hosting" href="/docs/managing-membrane/self-hosting/google-cloud-platform" target="_blank">
    Guide for deploying Membrane on Google Cloud Platform.
  </Card>
</Columns>

## Resource Limiting

To enable resource limits by workspace and tenant:

1. Add `ENABLE_LIMITS=1` to the API container
2. Add `NEXT_PUBLIC_ENABLE_LIMITS=1` to the Console container

This enables workspace managers to set tenant-level limits and platform administrators to configure workspace-level resource restrictions.

## FAQ and Advanced Configuration

### Self-Hosting Limitations

* **Managed OAuth Credentials**: Auth-proxy (managed credentials) is not supported. Membrane provides managed OAuth credentials for some apps in the cloud version, but this feature is not available in self-hosted deployments.
* **Pre-built Connectors**: The full library of pre-built connectors is not automatically available. You need to [manually migrate connectors](https://community.getmembrane.com/t/how-to-access-pre-built-connectors-in-a-self-hosted-deployment/71) from cloud to self-hosted environments using the Membrane CLI.

### Resource Requirements

* **Minimum Requirements**: 500 millicores (0.5 CPU) and 2GB of memory per container is sufficient for most deployments.

### Data Persistence and Backups

* **MongoDB**: Requires regular backups
* **S3 Storage**: All buckets should be backed up regularly
* **Redis**: Used only as a cache; can be safely rebooted or erased

### Health Monitoring

* **HTTP Health Checks**: The root endpoint of each service (e.g., `https://api.yourdomain.com/`) serves as a health check endpoint
* **Worker Health Checks**: Workers and custom code runners also expose an HTTP server at their root endpoint for health monitoring

### Security

* We monitor containers daily with the following SLAs:
  * Critical issues: 1 business day
  * High severity issues: 3 business days
  * Other issues: 2 weeks

### Logging and Error Handling

* **Log Format**: Services log to stdout/stderr in plain text
