Run locally with Docker
One command brings up Postgres, storage and LiveKit on your machine — the only thing left to fill in by hand is Discord.
Running locally is the fastest way to see OpenCall working: nothing is provisioned in the cloud, nothing costs anything, and you can break whatever you want. It is also the setup to use if you intend to change the code.
docker compose brings up the three pieces of infrastructure the app depends
on — Postgres, MinIO (S3-compatible, standing in for
Cloudflare R2) and a LiveKit server (standing in for LiveKit Cloud). The app
itself, Next.js, runs on your machine with pnpm dev, outside the containers,
so hot reload keeps working while you edit.
Discord is the one thing that stays manual
Login goes through Discord's servers, so it cannot be containerized: you need
a Discord application of your own, with http://localhost:3000 registered as
the redirect. It is step 2 below and takes a couple of minutes.
What you need
-
Docker Desktop running — docker.com/get-started. On Windows it asks to enable WSL 2 during install; accept it.
-
Node.js, which is what provides
npm— see "Before you start" in Requirements. -
pnpm, the package manager the repo uses:
npm install -g pnpm -
A fork of the repository, from GitHub repository.
1. Clone your fork
Replace YOUR-USERNAME with your GitHub username.
git clone https://github.com/YOUR-USERNAME/OpenCall.gitcd OpenCallThen install the dependencies:
pnpm install2. Create the Discord application
Follow Set up Discord, with one difference: the Redirect URI is the local address, not a domain.
http://localhost:3000/api/auth/callback/discordKeep the Client ID and the Client Secret at hand — they go into the next step.
3. Fill in the .env
The repo ships a .env.docker.example whose values already match
docker-compose.yml. Copy it:
cp .env.docker.example .envOpen the .env and fill in the only two empty lines that matter:
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=Everything else — database, LiveKit, MinIO — is already pointing at the
containers, and NEXTAUTH_SECRET comes with a development value that is fine
locally.
It has to be .env, not .env.local
Next.js reads both, but the Prisma CLI only reads .env. If the file has the
other name, pnpm dev works and the migration fails with Environment variable not found: DATABASE_URL.
4. Bring it up
With Docker Desktop open:
pnpm dev:dockerThat single command does three things in order: brings up the containers
(pnpm docker:up), creates the tables in Postgres (prisma migrate dev) and
starts Next.js (pnpm dev). When it finishes it prints the address:
http://localhost:3000minio-init exiting is not an error
Among the containers there is a one-shot job that creates the bucket and
makes it publicly readable. It runs once and stops — exited (0) in Docker
Desktop is what success looks like for it.
5. First login
Open localhost:3000 and log in with Discord.
The first account to log in while the database is empty is created as ADMIN,
and a text channel and a voice channel named Geral are created with it. Since
this is your machine, that first account is yours.
Day to day
| Command | What it does |
|---|---|
pnpm dev:docker | Infrastructure + migrations + app, all at once |
pnpm dev | Only the app, when the containers are already up |
pnpm docker:up | Only the containers |
pnpm docker:down | Stops the containers, keeps the data |
docker compose down -v | Stops and wipes the database and the uploaded files |
What is listening where
| Piece | Address | Credentials |
|---|---|---|
| App (Next.js) | http://localhost:3000 | — |
| Postgres | localhost:5433 | opencall / opencall, database opencall |
| MinIO (S3 API) | http://localhost:9000 | opencall / opencall123 |
| MinIO (web console) | http://localhost:9001 | same as above |
| LiveKit | ws://localhost:7880 | devkey / secret |
Postgres is on 5433, not the usual 5432, on purpose: it avoids clashing with a Postgres you may already have installed on the machine.
Limits of the local setup
- Voice and video only work on this machine. The LiveKit container
announces
127.0.0.1as its ICE candidate, so another device on the same Wi-Fi opens the page but never connects to a call. To test with other people, use the Demo deploy (Render). - This is not the production deploy. The
docker-compose.ymlis development infrastructure with fixed keys and passwords in plain sight. What goes to production is theDockerfileat the root of the repo, built by Railway or Render — see Production deploy (Railway). - GIFs in chat need a key.
GIPHY_API_KEYis empty in the example file; without it the GIF button simply does not show up and the rest of the chat works the same.
When something goes wrong
| Symptom | Cause |
|---|---|
Cannot connect to the Docker daemon | Docker Desktop is not running |
port is already allocated | Something else is on 3000, 9000 or 7880 — close it and try again |
Environment variable not found: DATABASE_URL | The file is named .env.local; it has to be .env |
Login returns invalid_redirect_uri | The Redirect URI in Discord does not match http://localhost:3000/api/auth/callback/discord exactly |
| Chat images fail to upload | The minio-init job did not run — pnpm docker:down and pnpm docker:up again |
Next step
To put an instance on the air for other people, without a card: Demo deploy (Render).