---
title: "Run from source | FoxAuth"
description: "Clone the repository, provision the database and start the server with Bun."
canonical: "https://foxauth.dev/docs/get-started/from-source/"
lastmod: "2026-10-15T18:51:11+03:00"
---
Run from source

Running from source is the usual path for development, for reading the code alongside a live
server, and for any deployment that wants to build its own image.

Prerequisites: Bun v1.3 or newer, and a database you can reach: MongoDB or
PostgreSQL, whichever you would rather run.

Clone and install:

Terminal window

git clone https://github.com/RedFox-Soft/OAuth-server.ts.git

cd OAuth-server.ts

bun install

Create a .env file with the required variables:

ISSUER=http://localhost:3000

MONGODB_URI=mongodb://localhost:27017

DATABASE_NAME=OAuth

For PostgreSQL instead, set POSTGRES_URL and leave the other two out. The database name is
part of that URL, so DATABASE_NAME is unused:

ISSUER=http://localhost:3000

POSTGRES_URL=postgres://user:password@localhost:5432/oauth

Set both connection strings and the server refuses to start instead of picking one; see
PostgreSQL. Bun loads .env automatically; nothing else needs
configuring. Each variable is described in the
environment variables reference. ISSUER is the canonical public
URL of this server: the iss of every token, the base of every endpoint in discovery, and the
redirect target of the admin console client — so set it to its final value before the next step.

Provision the database:

Terminal window

bun run db:setup # MongoDB

bun run db:setup:pg # PostgreSQL

Bundle the console and the sign-in screens into public/:

Terminal window

bun run build

Start the server:

Terminal window

bun start

It listens on port 3000. Open http://localhost:3000/admin for the console, or
http://localhost:3000/.well-known/openid-configuration for the discovery document.

What bun run db:setup provisionsSection titled “What bun run db:setup provisions”

db:setup runs database/mongodb.ts against MONGODB_URI / DATABASE_NAME, and it is what
turns an empty database into a working deployment:

Collections, one per storage area the server writes to.

Indexes: the TTL indexes that expire tokens, codes, sessions and interactions, and the unique
e-mail index on each project’s end-user collection. It also drops stale expiry indexes left on
collections that no longer write the field.

The initial RS256 signing key, stored with every issuer’s keys in the bucketKeys area, which
is why keys are not an environment variable here. (If the server ever starts against an empty key
store it generates and persists one itself.)

The admin panel seed: the reserved “Administrators” project with registration closed, the
first-party admin-panel OAuth client whose redirect URI is $ISSUER/admin/callback, the reserved
admin-mcp agent client, the System team that holds projects no administrator manages, and the
default redfox end-user project.

It is idempotent: every write is an upsert or a create-if-absent, so running it again changes
nothing that already exists. Re-run it after upgrading an existing install, since a release that
adds a collection, an index or a seeded document applies it here and nowhere else. Then run
bun run db:migrate, which applies whatever a release has to reshape, as opposed to merely add; the
server refuses to start until it has. Some migrations cannot be undone, so take a backup when the
release notes name one.

On PostgreSQL, bun run db:setup:pg does the same job with tables in place of collections. The only
structural difference is expiry: PostgreSQL has no TTL index, so the running server sweeps expired
rows itself. bun run db:migrate is the same command on either backend.

Note

db:setup exits non-zero when a constraint it declares is not in force: a unique index it could
not create because the data already violates it, for instance. A deployment pipeline should fail
the deploy on that exit code instead of logging a warning; the shipped fly.toml does exactly that.

NotesSection titled “Notes”

The admin console needs a database-backed deployment. Setting NODE_ENV=test swaps in the
in-memory adapter, which does not persist the seed across restarts.

Server settings are read at boot from the config store, and a change saved in the console applies
at once on the instance that saved it; other instances take it at their next restart. Every one of
them is listed in the settings reference.

PreviousRun with Docker ComposeNextCreate the first administrator
