---
title: Self-host SuperTokens
description: Deploy SuperTokens Core privately with Docker or on a VM and connect PostgreSQL for persistent storage.
sidebar:
  order: 1
---

## Self-hosting summary

- Deploy Core with its Docker image or directly on a VM.
- Core 11.0.0 dropped MySQL and MongoDB support; an in-memory database is available for testing. Confirm the supported PostgreSQL range for your exact release.
- Core listens on port 3567 by default. `/hello` normally performs a storage read, but rate-limited responses can return 200 without one; it is not a complete database-health or security check.
- Docker accepts either `POSTGRESQL_CONNECTION_URI` or separate PostgreSQL host, port, database, username, and password variables.

See how you can run **SuperTokens** in your own infrastructure.

---

## Overview

One of the main features of **SuperTokens** is that you can run it using your own resources.
This way you have full control over the authentication data and you can scale based on your needs.

## Before you start

To deploy the Core Service you must configure two things: the actual API and the database.
- The core service can be deployed using a **Docker** image or directly inside your VM.
- The supported database is **PostgreSQL**. Confirm the supported version range for the exact Core/database-plugin release you select.

:::danger
SuperTokens Core is a trusted backend component. It exposes APIs that can administer users, sessions, and tenants. Run
Core and PostgreSQL on private networks reachable only by trusted backend services; never expose either directly to a
browser or any client you do not trust. Core has no API key by default. Configure a generated API key, firewall/security-group
rules, and TLS at a trusted proxy or load balancer as defense in depth. Tenant isolation must be enforced by your backend;
a shared Core API key does not authorize an end user for a tenant. See [Secure the core](#secure-the-core) for details.
:::

The exact PostgreSQL support range and current Core/database artifact mapping are not established by this guide. Verify
both for the immutable release selected for production.

:::info[**SuperTokens Core** has dropped **MySQL** and **MongoDB** support with the `11.0.0` release.]
If you want to reference the old documentation, please [open this page](/legacy/core/v10/self-host-supertokens).
:::

## Steps

### 1. Install SuperTokens core

#### With Docker

Do not use an untagged image or `latest`. Select and verify an exact supported Core image, pin it by digest, and set it as
`SUPERTOKENS_IMAGE`. For a local-only in-memory test, bind Core to `127.0.0.1`:

```bash
: "${SUPERTOKENS_IMAGE:?Set an immutable image reference such as repository:version@sha256:digest}"
docker run -p 127.0.0.1:3567:3567 -d "$SUPERTOKENS_IMAGE"
```

Omitting PostgreSQL configuration starts the container with an in-memory database. Use this only for testing.

#### Without Docker

##### 1. Download SuperTokens

1. **Visit the open source download page**

    Open the [open source download page](https://SuperTokens.com/use-oss).

2. **Click on the Binary tab**

3. **Choose your database**

4. **Download the SuperTokens zip file for your OS**

After downloading, verify the release checksum or signature and extract the archive. You should see a folder named `supertokens`.

##### 2. Install SuperTokens

<CodeGroup group="operating-system">
<Tab title="Linux" value="linux">
```bash
# sudo is required so that the supertokens
# command can be added to your PATH variable.

cd supertokens
sudo ./install
```
</Tab>
<Tab title="Mac" value="mac">
```bash

cd supertokens
./install

```
</Tab>
<Tab title="Windows" value="windows">
```batch

Rem run as an Administrator. This is required so that the supertokens
Rem command can be added to your PATH.

cd supertokens
install.bat

```
</Tab>
</CodeGroup>

<DependentContent passive group="operating-system">
<ContentOption title="Mac" value="mac">
:::warning[You may get an error like `java cannot be opened because the developer cannot be verified`. To solve this, visit System Preferences > Security & Privacy > General Tab, and then click on the Allow button at the bottom. Then retry the command above.]
:::
</ContentOption>
</DependentContent>

:::note[After installing, you can delete the downloaded folder as you no longer need it.]

Make any changes to the configuration in the `config.yaml` file in the installation directory, as specified in the output of the `supertokens --help` command.
:::

##### 3. Start the core service

Running the following command starts the service.
```bash
supertokens start [--host=...] [--port=...]
```
- The above command starts the Core service using the configured database.
- To see all available options please run `supertokens start --help`

:::info[Tip]
To stop the service, run the following command:
```bash
supertokens stop
```
:::

### 2. Test that the service is running

Open a browser and visit `http://localhost:3567/hello`. If you see a page that says `Hello` back, then the container started successfully!

If you are having issues with starting the docker image, please feel free to reach out [over email](mailto:support@supertokens.com) or [via Discord](https://supertokens.com/discord).

:::tip
`/hello` normally performs a storage read and returns an error if that read fails. However, after its request-rate limit is
exhausted, it can return `200 Hello` without querying storage. It also deliberately requires no API key. Use it only as a
basic process/readiness signal, not as proof of database health, API-key enforcement, or safe network exposure. Pair it
with authenticated application checks and database monitoring; tune liveness separately to avoid restart loops.
:::

### 3. Connect the backend SDK with SuperTokens

- The default port for SuperTokens is `3567`. Keep it private. For local testing, bind it only to `127.0.0.1`, for example `-p 127.0.0.1:8080:3567`.
- The connection info goes in the `supertokens` object in the `init` function on your backend:

<CodeGroup group="backend-language">
<Tab title="Node.js" value="nodejs">
```tsx
import supertokens from "supertokens-node";

const apiKey = process.env.SUPERTOKENS_API_KEY;
if (apiKey === undefined || apiKey.length === 0) {
  throw new Error("SUPERTOKENS_API_KEY is required");
}

supertokens.init({
  supertokens: {
    connectionURI: "http://localhost:3567",
    apiKey,
  },
  appInfo: {
    apiDomain: "...",
    appName: "...",
    websiteDomain: "...",
  },
  recipeList: [],
});
```
</Tab>
<Tab title="Go" value="go">
```go
import (
	"os"

	"github.com/supertokens/supertokens-golang/supertokens"
)

func main() {
	apiKey := os.Getenv("SUPERTOKENS_API_KEY")
	if apiKey == "" {
		panic("SUPERTOKENS_API_KEY is required")
	}
	supertokens.Init(supertokens.TypeInput{
		Supertokens: &supertokens.ConnectionInfo{
			ConnectionURI: "http://localhost:3567",
			APIKey:        apiKey,
		},
	})
}

```
</Tab>
<Tab title="Python" value="python">
```python check=false reason="Partial configuration example"
import os

from supertokens_python import init, InputAppInfo, SupertokensConfig

api_key = os.environ["SUPERTOKENS_API_KEY"]
if not api_key:
    raise RuntimeError("SUPERTOKENS_API_KEY is required")

init(
    app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
    supertokens_config=SupertokensConfig(
        connection_uri='http://localhost:3567',
        api_key=api_key
    ),
    framework='...',
    recipe_list=[
      #...
   ]
)
```
</Tab>
</CodeGroup>

:::info[Configure the same generated secret in Core and every backend]
Generate a key with `openssl rand -hex 32`, store it in your secret manager, and inject it as Core's `API_KEYS` and the
`SUPERTOKENS_API_KEY` used by the backend. Never bake it into source code or an image. See the
[API-key documentation](/platform-configuration/supertokens-core/api-keys) for validation and rotation.
:::

### 4. Set up the database

#### 4.1 Create a database (optional)

```sql
CREATE DATABASE supertokens;
```

You can skip this step if you want SuperTokens to write to your own database.
In this case, you need to provide your database's name as shown in the step below.

#### 4.2 Connect SuperTokens to your database

##### With Docker

:::warning
Inside a container, `localhost` refers to that container. Attach Core and PostgreSQL to the same private Docker network,
or use a private DNS name/interface. Configure PostgreSQL `listen_addresses`, `pg_hba.conf`, host firewall, and cloud
security groups so that only Core can connect. Do not publish port 5432 or expose the database through a public address.
:::

:::warning[It is important to use the `postgresql://` scheme designator in the PostgreSQL Connection URI. Using `postgres://` will lead to a startup error.]
:::

```bash

: "${SUPERTOKENS_IMAGE:?Set an immutable Core image reference}"
: "${SUPERTOKENS_API_KEY:?Set a generated Core API key}"
docker run \
    --network app-network \
    -e POSTGRESQL_CONNECTION_URI="postgresql://username:pass@host/dbName" \
    -e API_KEYS="$SUPERTOKENS_API_KEY" \
    -d "$SUPERTOKENS_IMAGE"

# OR

docker run \
    --network app-network \
    -e POSTGRESQL_USER="username" \
    -e POSTGRESQL_PASSWORD="password" \
	-e POSTGRESQL_HOST="host" \
	-e POSTGRESQL_PORT="5432" \
    -e POSTGRESQL_DATABASE_NAME="supertokens" \
    -e API_KEYS="$SUPERTOKENS_API_KEY" \
    -d "$SUPERTOKENS_IMAGE"
```

:::tip[You can also provide the table schema by setting the `POSTGRESQL_TABLE_SCHEMA` option.]
:::

##### Without Docker

```yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

postgresql_connection_uri: "postgresql://username:pass@host/dbName"

# OR

postgresql_user: "username"

postgresql_password: "password"

postgresql_host: "host"

postgresql_port: "5432"

postgresql_database_name: "supertokens"
```

You can also provide the table schema by setting the `postgresql_table_schema` option.

:::info
Core creates and migrates its required tables automatically when the database principal has DDL permission. Do not use a
hand-copied schema: it can drift from the selected Core and database-plugin release. If your production principal cannot
perform DDL, obtain a schema or migration artifact generated for the exact immutable release, apply it with a separate
migration principal, and test Core startup before promotion.
:::

#### 4.3 Test the connection

Start the exact Core release against a staging copy of the database and require startup/migration success. Then exercise
an authenticated SDK operation. A query against one table does not prove that every required migration was applied.

#### 4.4 Rename database tables (optional)

:::warning[If you already have tables created by SuperTokens, and then you rename them, SuperTokens creates new tables. Please be sure to migrate the data from the existing one to the new one.]
:::

You can add a prefix to all table names that SuperTokens manages. This way, all will be renamed in a way that has no clashes with your tables.

For example, two tables created by SuperTokens have the names `emailpassword_users` and `thirdparty_users`. If you add a prefix to them (something like `"my_prefix"`), then the tables become `my_prefix_emailpassword_users` and `my_prefix_thirdparty_users`.

<CodeGroup group="docker">
<Tab title="With Docker" value="with-docker">
```bash
docker run \
    --network app-network \
    -e POSTGRESQL_TABLE_NAMES_PREFIX="my_prefix" \
    -e API_KEYS="$SUPERTOKENS_API_KEY" \
    -d "$SUPERTOKENS_IMAGE"
```
</Tab>
<Tab title="Without Docker" value="without-docker">
```yaml
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

postgresql_table_names_prefix: "my_prefix"
```
</Tab>
</CodeGroup>

### 5. Add license keys

To access some features in your self-hosted service you must use **license keys**.
You can sign up on [**SuperTokens**](https://supertokens.com/auth) to receive one.

Once you have the license key you need to manually add it to your **SuperTokens Core Instance**.
To do this you have to call the Core API with the following request:

```bash title="Add License Key"
curl --location --request PUT "${CORE_API_ENDPOINT:?Set the private Core endpoint}/ee/license" \
     --header 'Content-Type: application/json' \
     --header "api-key: ${SUPERTOKENS_API_KEY:?Set the Core API key}" \
     --data-raw "{ \"licenseKey\": \"${SUPERTOKENS_LICENSE_KEY:?Set the license key}\" }"

```

## Secure the core

The SuperTokens Core exposes administrative operations over its API — creating and updating users, issuing password-reset and passwordless codes, managing tenants, and more. By design these are available to the connecting backend, because the Core has no direct channel to your frontend and relies on your backend to mediate every request and to deliver codes and tokens to end users.

This trust model means the Core must be treated like your database: reachable only by your own backend, never by untrusted clients.

- **Isolate the network.** Run the Core on a private network or subnet that only your backend can reach. This is the primary protection and applies regardless of any other setting.
- **Set an API key.** No API key exists by default, so any caller that can reach an unprotected Core can perform administrative operations. Configure a generated [API key](/platform-configuration/supertokens-core/api-keys) as defense in depth. Core supports multiple keys for rotation, but these are not per-tenant authorization credentials and do not replace network isolation.
- **Restrict by IP and use TLS.** Limit access with firewall/security-group rules and, optionally, Core's [IP allow/deny configuration](/platform-configuration/supertokens-core/ip-allow-deny). Terminate [TLS/SSL](/platform-configuration/supertokens-core/add-ssl-via-nginx) at a trusted proxy or load balancer.
- **Enforce tenant scoping in your backend.** For session-authenticated requests restricted to a specific tenant, verify the session and check that its tenant matches the required tenant. Authorize access to tenant-specific resources in your backend; do not rely on the URL alone.
