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

# Deploy the Open-Source Blnk Core

> Learn how to set up and deploy the open-source Blnk Core in your own environment.

export const CtaCallout = props => {
  const {title, buttonLabel, href, trackingEvent, buttonTarget, rel = "noopener noreferrer", children} = props;
  const handleCtaClick = () => {
    if (typeof window === "undefined" || !trackingEvent) {
      return;
    }
    try {
      window.dispatchEvent(new CustomEvent("blnk:docs-cta", {
        detail: {
          name: trackingEvent,
          href
        }
      }));
    } catch {}
    try {
      window.posthog?.capture?.(trackingEvent, {
        href
      });
    } catch {}
    const gaPayload = {
      cta_href: href
    };
    try {
      window.gtag?.("event", trackingEvent, gaPayload);
    } catch {}
    try {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: trackingEvent,
        ...gaPayload
      });
    } catch {}
  };
  const isExternal = typeof href === "string" && (/^https?:\/\//i).test(href);
  const target = buttonTarget ?? (isExternal ? "_blank" : undefined);
  const linkRel = isExternal ? rel : undefined;
  return <section className="cta-callout not-prose relative my-8 w-full min-w-0 overflow-hidden rounded-xl border border-zinc-200 p-5 dark:border-white/10">
      <div className="cta-callout-noise" aria-hidden="true" />
      <div className="cta-callout-layout">
        {title ? <div className="cta-callout-title-row">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="14" height="14" className="cta-callout-icon shrink-0 text-zinc-800 dark:text-zinc-200" aria-hidden="true">
              <g fill="none" fillRule="nonzero">
                <path d="M28 0v28H0V0h28ZM14.691833333333335 27.134333333333334l-0.012833333333333334 0.0023333333333333335 -0.08283333333333333 0.04083333333333334 -0.023333333333333334 0.004666666666666667 -0.016333333333333335 -0.004666666666666667 -0.08283333333333333 -0.04083333333333334c-0.011666666666666667 -0.004666666666666667 -0.022166666666666668 -0.0011666666666666668 -0.028000000000000004 0.005833333333333334l-0.004666666666666667 0.011666666666666667 -0.019833333333333335 0.49933333333333335 0.005833333333333334 0.023333333333333334 0.011666666666666667 0.015166666666666667 0.12133333333333333 0.08633333333333333 0.0175 0.004666666666666667 0.014000000000000002 -0.004666666666666667 0.12133333333333333 -0.08633333333333333 0.014000000000000002 -0.018666666666666668 0.004666666666666667 -0.019833333333333335 -0.019833333333333335 -0.4981666666666667c-0.0023333333333333335 -0.011666666666666667 -0.0105 -0.019833333333333335 -0.019833333333333335 -0.021Zm0.3091666666666667 -0.13183333333333336 -0.015166666666666667 0.0023333333333333335 -0.21583333333333335 0.1085 -0.011666666666666667 0.011666666666666667 -0.0035000000000000005 0.012833333333333334 0.021 0.5016666666666667 0.005833333333333334 0.014000000000000002 0.009333333333333334 0.008166666666666668 0.23450000000000004 0.1085c0.014000000000000002 0.004666666666666667 0.026833333333333334 0 0.03383333333333334 -0.009333333333333334l0.004666666666666667 -0.016333333333333335 -0.03966666666666667 -0.7163333333333334c-0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.023333333333333334 -0.023333333333333334 -0.025666666666666667Zm-0.8341666666666667 0.0023333333333333335a0.026833333333333334 0.026833333333334334 0 0 0 -0.0315 0.007000000000000001l-0.007000000000000001 0.016333333333333335 -0.03966666666666667 0.7163333333333334c0 0.014000000000000002 0.008166666666666668 0.023333333333333334 0.019833333333333335 0.028000000000000004l0.0175 -0.0023333333333333335 0.23450000000000004 -0.1085 0.011666666666666667 -0.009333333333333334 0.004666666666666667 -0.012833333333333334 0.019833333333333335 -0.5016666666666667 -0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.011666666666666667 -0.21466666666666667 -0.10733333333333334Z" strokeWidth="1.1667" />
                <path fill="currentColor" d="M14 2.916666666666667A1.75 1.75 0 0 1 15.750000000000002 4.666666666666667v6.302333333333334L21.207666666666668 7.816666666666667a1.75 1.75 0 0 1 1.75 3.031L17.5 14l5.457666666666667 3.151166666666667a1.75 1.75 0 0 1 -1.75 3.031l-5.457666666666667 -3.1500000000000004V23.333333333333336a1.75 1.75 0 0 1 -3.5 0v-6.302333333333334L6.792333333333334 20.183333333333337a1.75 1.75 0 1 1 -1.75 -3.031L10.5 14 5.042333333333334 10.848833333333333a1.75 1.75 0 0 1 1.75 -3.031l5.457666666666667 3.1500000000000004V4.666666666666667A1.75 1.75 0 0 1 14 2.916666666666667Z" strokeWidth="1.1667" />
              </g>
            </svg>
            <p className="cta-callout-title min-w-0 font-semibold text-zinc-800 dark:text-zinc-200">
              {title}
            </p>
          </div> : null}
        <div className={`cta-callout-body text-sm leading-normal text-zinc-800 dark:text-zinc-200${title ? " cta-callout-body--indented" : ""}`}>
          {children}
        </div>
        <a href={href} target={target} rel={linkRel} onClick={handleCtaClick} data-docs-cta={trackingEvent || undefined} className="cta-callout-button inline-flex items-center justify-center gap-1 rounded-full bg-white px-3 py-1.5 text-sm font-semibold transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 dark:bg-white dark:hover:bg-zinc-200">
          {buttonLabel}
          <span className="cta-callout-button-arrow" aria-hidden="true">
            →
          </span>
        </a>
      </div>
    </section>;
};

Self-host the open-source Blnk Core in infrastructure your team controls.

You are responsible for deployment, upgrades, configuration, and day-to-day operations across each environment you maintain-dev, staging, production, and beyond.

<CtaCallout title="Need help with your self-hosted Core?" href="https://blnkfinance.com/contact/us?utm_source=blnk_docs&utm_medium=documentation&utm_campaign=home%2Fdeploy" buttonLabel="Get Pro Support" trackingEvent="clicked_pro_support">
  If you'd like help integrating your self-hosted Core into your product, reach out for Pro Support.
</CtaCallout>

***

## Deployment guide

There are three ways to self-host Blnk Core in your environment:

* **Single server:** Use Docker Compose on a Linux VM you control, e.g. a DO Droplet, EC2, GCE, or Azure VM.
* **Platform-as-a-Service (PaaS):** Use a platform hosting service like Railway, Render, Fly.io, etc.
* **Kubernetes:** Use Kubernetes to deploy Blnk Core.

<Tabs>
  <Tab title="Single server">
    Use Docker Compose on a Linux VM you control; for example a DO Droplet, EC2, GCE, or Azure VM.

    Before you start, make sure you have the following set up (required):

    1. Managed PostgreSQL instance
    2. Managed Redis instance
    3. Docker and Docker Compose installed
    4. Linux server (Ubuntu 20.04 LTS or newer recommended)

    <Steps>
      <Step title="Set up your configuration file">
        This is the configuration file that will be used to start the Blnk Core, it can be the `blnk.json` file or environment variables.

        For more options, go to [Blnk Configuration](/advanced/configuration/overview).

        <CodeGroup>
          ```json blnk.json theme={"system"}
          {
            "project_name": "Blnk",
            "data_source": {
              "dns": "postgres://<user>:<password>@<host>:5432/blnk?sslmode=require"
            },
            "redis": {
              "dns": "redis://:<password>@<host>:6379"
            },
            "server": {
              "secure": true,
              "secret_key": "set-your-master-blnk-key-here",
              "port": "5001"
            },
            "queue": {
              "monitoring_port": "5004"
            }
          }
          ```

          ```bash .env theme={"system"}
          BLNK_PROJECT_NAME=Blnk
          BLNK_DATA_SOURCE_DNS='postgres://<user>:<password>@<host>:5432/blnk?sslmode=require'
          BLNK_REDIS_DNS='redis://:<password>@<host>:6379'
          BLNK_SERVER_SECURE=true
          BLNK_SERVER_SECRET_KEY='set-your-master-blnk-key-here'
          BLNK_SERVER_PORT=5001
          BLNK_QUEUE_MONITORING_PORT=5004
          ```
        </CodeGroup>

        <Note>
          If you use a secrets manager (AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, etc.), fetch secrets onto the VM and write them to `.env` before you run `docker compose up`.
        </Note>
      </Step>

      <Step title="Create a 'docker-compose.yml' file">
        Add the following configuration to provision the server, worker, and supporting services if needed. You can use the default `docker-compose.yml` file in the [Blnk repository](https://github.com/blnkfinance/blnk/blob/main/docker-compose.yaml).

        <Note>
          For live deployments, we recommend connecting your managed Postgres and Redis databases. This means you can exclude the `redis` and `postgres` services from your `docker-compose.yaml` file.
        </Note>

        ```yaml docker-compose.yaml wrap expandable theme={"system"}
        version: "3.8"

        services:
          server:
            image: ${BLNK_IMAGE:-jerryenebeli/blnk:0.15.0}
            container_name: server
            restart: on-failure
            entrypoint: ["/bin/sh", "-c"]
            command:
              - "blnk migrate up && blnk start"
            environment:
              TZ: ${TZ:-Etc/UTC}
            env_file:
              - .env
            ports:
              - "5001:5001"
              - "80:80"
              - "443:443"
            volumes:
              - ./blnk.json:/blnk.json

          worker:
            image: ${BLNK_IMAGE:-jerryenebeli/blnk:0.15.0}
            container_name: worker
            restart: on-failure
            entrypoint: ["blnk", "workers"]
            env_file:
              - .env
            ports:
              - "5004:5004"
            volumes:
              - ./blnk.json:/blnk.json
        ```
      </Step>

      <Step title="Start deployment">
        Run these commands in your terminal to start and validate your deployment:

        ```shell theme={"system"}
        # Start all services
        docker compose up -d

        # Verify deployment status
        docker ps

        # Monitor service logs
        docker logs -f server worker
        ```

        <Check>
          Blnk should be running and accessible at port 5001 and available for requests.
        </Check>
      </Step>
    </Steps>
  </Tab>

  <Tab title="PaaS (Railway, etc.)">
    Deploy Blnk Core on a platform hosting service like Railway, Render, or Fly.io.

    <Steps>
      <Step title="Set up Postgres and Redis">
        In your project, provision a managed PostgreSQL database and a managed Redis instance if you do not already have them.

        You can also connect to a third-party database if you prefer. Make sure that the database and Redis are reachable from the platform.

        Copy each connection string from your provider. You will use them in the next step.

        <Note>
          If Postgres and Redis run in the same project as Blnk, use the provider's **internal** connection URLs for lower latency and to avoid public network egress.
        </Note>
      </Step>

      <Step title="Set environment variables">
        Add these environment variables to your project. Use your managed Postgres and Redis connection strings.

        ```bash .env wrap theme={"system"}
        BLNK_PROJECT_NAME=Blnk
        BLNK_DATA_SOURCE_DNS='postgres://<user>:<password>@<host>:5432/blnk?sslmode=require'
        BLNK_REDIS_DNS='redis://:<password>@<host>:6379'
        BLNK_SERVER_SECURE=true
        BLNK_SERVER_SECRET_KEY='set-your-master-blnk-key-here'
        BLNK_SERVER_PORT=5001
        BLNK_QUEUE_MONITORING_PORT=5004
        ```

        You can add more environment variables later as you need, go to [Blnk Configuration](/advanced/configuration/overview).

        <Note>
          Use the **direct** Postgres connection string, not a pooled URL, if your provider offers both.
        </Note>
      </Step>

      <Step title="Deploy the server">
        Create a service from the Blnk Docker image:

        * **Docker image:** `jerryenebeli/blnk:0.15.0`
        * **Start command:** `blnk migrate up && blnk start`
        * **Port:** `5001`

        Connect this service to the environment variables from the previous step, or copy them into the service settings.

        Point the server public URL to port `5001` to make it accessible from outside the project.

        Deploy the server and wait for migrations to finish in the logs.
      </Step>

      <Step title="Deploy the worker">
        Create a second service using the same Docker image and the **same environment variables** as the server.

        * **Docker image:** `jerryenebeli/blnk:0.15.0`
        * **Start command:** `blnk workers`

        The worker does not need a public HTTP port. Optionally, point its public URL to port `5004` if you want to access the [monitoring dashboard](/advanced/monitoring-port).

        Deploy the worker once you're done.
      </Step>

      <Step title="Verify deployment">
        Check the server logs for a successful migration and startup. Send a request to your service URL:

        ```bash theme={"system"}
        curl https://<your-server-url>/health
        ```

        Confirm the worker service is running in its logs.

        <Check>
          Blnk should now be running and accessible on your platform's public URL.
        </Check>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Kubernetes">
    Blnk provides Kubernetes manifests to help you deploy and operate Blnk in containerized environments.

    Use the official manifests and instructions:

    <Card href="https://github.com/blnkfinance/blnk/tree/main/infrastructure/k8s-manifests" icon="folder-git" title="infrastructure/k8s-manifests">
      Official manifests and deploy steps.
    </Card>

    After `kubectl` is installed, run:

    ```bash kubectl theme={"system"}
    kubectl apply -f infrastructure/k8s-manifests/namespace.yaml
    kubectl apply -f infrastructure/k8s-manifests
    ```
  </Tab>
</Tabs>

***

## Back-office management

Once your Blnk Core is deployed in your own environment, you can connect it to a Blnk Cloud account to operate your ledger from a back-office dashboard.

<img src="https://mintcdn.com/blnk/V1zKD8N18ifO090g/cloud/img/transactions/transactions-table.png?fit=max&auto=format&n=V1zKD8N18ifO090g&q=85&s=64fe833e81988b573fb46f14fecdf315" alt="Transactions table" className="rounded-lg" width="1366" height="768" data-path="cloud/img/transactions/transactions-table.png" />

<CardGroup cols={2}>
  <Card title="Connect Core to Blnk Cloud" href="/cloud/instances/create" icon="link">
    Self-hosted Core connected to Cloud.
  </Card>

  <Card title="Monitoring" href="/advanced/monitoring" icon="activity">
    Use Blnk Cloud for monitoring
  </Card>
</CardGroup>

***

## Setting up monitoring

We recommend connecting to Blnk Cloud to set up monitoring for your Core in less than 5 minutes. See [Quick setup: Blnk Cloud](/advanced/monitoring#quick-setup-blnk-cloud).

To manually export logs, traces, and metrics for your local deployment:

<Steps>
  <Step title="Enable observability in Core">
    Enable observability in your configuration file:

    <CodeGroup>
      ```bash .env wrap theme={"system"}
      BLNK_ENABLE_OBSERVABILITY=true
      ```

      ```json blnk.json wrap theme={"system"}
      {
        "enable_observability": true
      }
      ```
    </CodeGroup>

    Restart server and worker after you finish the compose changes below.
  </Step>

  <Step title="Add Prometheus config">
    Create `prometheus.yml` beside your compose file:

    ```yaml prometheus.yml wrap theme={"system"}
    global:
      scrape_interval: 15s

    scrape_configs:
      - job_name: 'blnk-server'
        authorization:
          type: Bearer
          credentials: '<metrics-bearer-token>'
        static_configs:
          - targets: ['server:5001']
      - job_name: 'blnk-worker'
        authorization:
          type: Bearer
          credentials: '<metrics-bearer-token>'
        static_configs:
          - targets: ['worker:5004']
    ```
  </Step>

  <Step title="Add Jaeger and Prometheus services">
    Update your `docker-compose.yaml` to include Jaeger and Prometheus. This example extends the [base compose](#deployment-guide) with trace export on server and worker:

    ```yaml docker-compose.yaml {13,18-19,28-29,32-33,37-49,51-62} wrap expandable theme={"system"}
    version: "3.8"

    services:
      server:
        image: ${BLNK_IMAGE:-jerryenebeli/blnk:0.15.0}
        container_name: server
        restart: on-failure
        entrypoint: ["/bin/sh", "-c"]
        command:
          - "blnk migrate up && blnk start"
        environment:
          TZ: ${TZ:-Etc/UTC}
          OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://jaeger:4318/v1/traces
        env_file:
          - .env
        ports:
          - "5001:5001"
          - "80:80"
          - "443:443"
        depends_on:
          - jaeger
        volumes:
          - ./blnk.json:/blnk.json

      worker:
        image: ${BLNK_IMAGE:-jerryenebeli/blnk:0.15.0}
        container_name: worker
        restart: on-failure
        entrypoint: ["blnk", "workers"]
        environment:
          OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: http://jaeger:4318/v1/traces
        env_file:
          - .env
        ports:
          - "5004:5004"
        depends_on:
          - jaeger
        volumes:
          - ./blnk.json:/blnk.json

      jaeger:
        image: jaegertracing/all-in-one:latest
        container_name: jaeger
        ports:
          - "16686:16686"
          - "4318:4318"
        environment:
          - COLLECTOR_OTLP_ENABLED=true
        healthcheck:
          test: ["CMD", "wget", "--spider", "http://localhost:16686"]
          interval: 10s
          timeout: 5s
          retries: 3

      prometheus:
        image: prom/prometheus:latest
        container_name: prometheus
        profiles:
          - monitoring
        ports:
          - "9090:9090"
        volumes:
          - ./prometheus.yml:/etc/prometheus/prometheus.yml
        depends_on:
          - server
          - worker
    ```
  </Step>

  <Step title="Start or restart the stack">
    Run `docker compose --profile monitoring up -d` to start Core, Jaeger, and Prometheus together.

    Open Jaeger at `http://localhost:16686` and Prometheus at `http://localhost:9090` to confirm data is flowing.

    To learn more, see [Monitoring in Blnk](/advanced/monitoring).
  </Step>
</Steps>

***

## Troubleshooting

Use this guide to troubleshoot your deployment when Blnk fails to start or you experience errors in your deployment.

<AccordionGroup>
  <Accordion title="Server fails to start or cannot reach PostgreSQL">
    **Common symptoms:** Connection refused or timeout errors in server logs; messages referencing `data_source` or `BLNK_DATA_SOURCE_DNS`.

    <Steps>
      <Step title="Verify your connection string">
        Confirm `BLNK_DATA_SOURCE_DNS` (or `data_source.dns` in `blnk.json`) includes the correct host, port, database name, user, and password.

        Set `sslmode` to match your provider; most managed Postgres instances require `sslmode=require`.

        <Warning>
          Many hosted Postgres providers expose two URLs: a **direct** connection and a **pooled** connection.

          If Blnk keeps timing out or disconnecting under load, confirm you are using the **direct** connection string for `BLNK_DATA_SOURCE_DNS`.
        </Warning>

        See [Database settings](/advanced/configuration/data-stores#database-settings).
      </Step>

      <Step title="Check network access">
        Allow inbound traffic from your Blnk host to PostgreSQL. Review security groups, firewall rules, and private networking (VPC peering, subnet routing) between the container host and your database.

        <Warning>
          If Postgres is only reachable on a private network, running `psql` from your laptop may succeed or fail independently of the container. Test from the same network context as the Blnk server.
        </Warning>
      </Step>

      <Step title="Test the connection">
        From a host on the same network as Blnk, run:

        ```bash theme={"system"}
        psql "$BLNK_DATA_SOURCE_DNS" -c "SELECT 1"
        ```

        A successful response confirms credentials and reachability.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Migrations fail on startup">
    **Common symptoms:** The server container exits immediately; logs show errors from `blnk migrate up`.

    The Docker Compose example runs migrations before starting the API (`blnk migrate up && blnk start`). Migration failures prevent the server from listening on port `5001`.

    <Steps>
      <Step title="Confirm database permissions">
        The database user must be able to create and alter tables in the target database. Read-only or restricted roles cause migration failures.

        <Check>
          Grant the application user `CREATE` and `ALTER` privileges on the `blnk` database (or your chosen database name).
        </Check>
      </Step>

      <Step title="Inspect migration logs">
        Read the server container output:

        ```bash theme={"system"}
        docker logs server
        ```

        Look for the first migration error-later messages often repeat the same root cause.
      </Step>

      <Step title="Run migrations manually">
        Re-run migrations in the foreground to capture the full error:

        ```bash theme={"system"}
        docker compose run --rm server blnk migrate up
        ```
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="Transactions stay queued or workers are idle">
    **Common symptoms:** The API accepts requests but transactions remain `QUEUED`; the worker container restarts or logs Redis connection errors.

    Workers depend on Redis for queueing and coordination. The server and worker must share the same Redis and [queue configuration](/advanced/configuration/queue).

    <Steps>
      <Step title="Verify Redis connectivity">
        Confirm `BLNK_REDIS_DNS` (or `redis.dns` in `blnk.json`) points at your managed Redis instance. Include the password in the URL when required:

        ```bash theme={"system"}
        redis://:<password>@<host>:6379
        ```

        For TLS-enabled Redis, use `rediss://` and review [Redis configuration](/advanced/configuration/data-stores#redis-configuration).
      </Step>

      <Step title="Confirm the worker is running">
        Both `server` and `worker` services must be up:

        ```bash theme={"system"}
        docker ps
        docker logs -f worker
        ```

        On Kubernetes, check worker pod status and logs alongside the server deployment.
      </Step>

      <Step title="Check queue health">
        If you configured `BLNK_QUEUE_MONITORING_PORT` (default `5004`), open the monitoring dashboard or probe the endpoint:

        ```bash theme={"system"}
        curl -s http://localhost:5004/monitoring
        ```

        See [Queue monitoring](/advanced/monitoring-port) for dashboard details and metrics.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="API is unreachable or containers keep restarting">
    **Common symptoms:** `docker ps` shows a restarting `server` or `worker` container; requests to port `5001` time out.

    <Steps>
      <Step title="Review container status">
        List all containers, including stopped ones:

        ```bash theme={"system"}
        docker ps -a
        docker logs -f server worker
        ```

        Restart loops usually point to configuration, migration, or dependency errors in the logs.
      </Step>

      <Step title="Confirm port exposure">
        Verify port `5001` is published in `docker-compose.yaml` and allowed through your host firewall and cloud security groups.

        <Check>
          `curl http://localhost:5001/health` (or your health endpoint) should respond when the server is healthy.
        </Check>
      </Step>

      <Step title="Validate configuration is loaded">
        When using `blnk.json`, confirm the file is mounted into both `server` and `worker` containers. When using environment variables, set them on both services-mismatched config between server and worker causes subtle queue issues.

        See [Blnk configuration](/advanced/configuration/overview).
      </Step>
    </Steps>

    <Tip>
      For PaaS deployments (Railway, Render, Fly.io), run the server and worker as separate services with identical configuration and the correct start commands for each.
    </Tip>
  </Accordion>
</AccordionGroup>
