Skip to main content

Overview

This guide covers setting up the complete Modrinth development environment, including the backend API (Labrinth) and all required services.

Prerequisites

Before you begin, ensure you have:

Services Overview

Modrinth uses several backend services:
ServicePurposePort
PostgreSQLPrimary database5432
RedisCache and sessions6379
ClickHouseAnalytics database8123
MeilisearchSearch engine7700
MailpitEmail testing8025 (UI), 1025 (SMTP)
GotenbergPDF generation13000
LabrinthAPI server8000
All services are defined in docker-compose.yml at the root of the repository.

Quick Start

1

Clone Repository

git clone https://github.com/modrinth/code.git
cd code
2

Install Dependencies

pnpm install
3

Start Services

docker compose up -d
This starts PostgreSQL, Redis, ClickHouse, Meilisearch, Mailpit, and Gotenberg.
4

Verify Services

Check that all services are healthy:
docker compose ps
All services should show status “healthy”.

Service Details

PostgreSQL

Image: postgres:15-alpine
Port: 5432
Database: labrinth
User: labrinth
Password: labrinth

Accessing PostgreSQL

# Via docker exec
docker exec labrinth-postgres psql -U labrinth -d labrinth

# Run a query
docker exec labrinth-postgres psql -U labrinth -d labrinth -c "SELECT COUNT(*) FROM projects;"

# Using local psql client
psql postgresql://labrinth:labrinth@localhost:5432/labrinth

Database Structure

Labrinth uses SQLx migrations located in apps/labrinth/migrations/.
# Run migrations (from apps/labrinth)
cd apps/labrinth
cargo sqlx migrate run

# Create new migration
cargo sqlx migrate add create_my_table

Redis

Image: redis:alpine
Port: 6379

Accessing Redis

# Connect to Redis CLI
docker exec -it labrinth-redis redis-cli

# Example commands
> KEYS *
> GET session:abc123
> HGETALL user:456

Usage in Labrinth

  • Session storage
  • Rate limiting counters
  • Temporary caches
  • Background job queues

ClickHouse

Image: clickhouse/clickhouse-server
Port: 8123 (HTTP)
Database: staging_ariadne
User: default
Password: default

Accessing ClickHouse

# Connect to ClickHouse client
docker exec -it labrinth-clickhouse clickhouse-client

# Use staging database
USE staging_ariadne;

# Query analytics
SELECT COUNT(*) FROM downloads;

Creating Tables

CREATE TABLE IF NOT EXISTS staging_ariadne.downloads (
    version_id String,
    timestamp DateTime,
    ip String,
    user_agent String
) ENGINE = MergeTree()
ORDER BY timestamp;

Meilisearch

Image: getmeili/meilisearch:v1.12.0
Port: 7700
Master Key: modrinth

Accessing Meilisearch

Web UI: http://localhost:7700
API Key: modrinth
# Check health
curl http://localhost:7700/health

# List indexes
curl http://localhost:7700/indexes \
  -H "Authorization: Bearer modrinth"

Indexing Projects

Labrinth automatically indexes projects to Meilisearch on creation/update.
# Manually trigger reindex (from Labrinth API)
curl -X POST http://localhost:8000/_internal/search/reindex \
  -H "Authorization: Bearer YOUR_ADMIN_KEY"

Mailpit (Email Testing)

Image: axllent/mailpit:v1.27
SMTP Port: 1025
Web UI Port: 8025

Viewing Emails

Open http://localhost:8025 to view all emails sent by Labrinth during development. Use cases:
  • Email verification
  • Password reset emails
  • Notification emails

Gotenberg (PDF Generation)

Image: gotenberg/gotenberg:8
Port: 13000
Used for generating PDF reports and invoices.
# Test PDF generation
curl --request POST \
  --url http://localhost:13000/forms/chromium/convert/url \
  --form url=https://modrinth.com \
  --output test.pdf

Running Labrinth (Backend API)

Setup

1

Navigate to Labrinth

cd apps/labrinth
2

Copy Environment File

cp .env.docker-compose .env
This file contains pre-configured settings for local development.
3

Run Migrations

cargo sqlx migrate run
This creates all necessary database tables.
4

Start Labrinth

cargo run -p labrinth
Or for faster compilation (debug mode):
cargo run
Labrinth will be available at http://localhost:8000

Verify API is Running

# Check health endpoint
curl http://localhost:8000/health

# Browse API docs
open http://localhost:8000/docs

Environment Variables

Key variables in .env.docker-compose:
# Database
DATABASE_URL=postgres://labrinth:labrinth@localhost/labrinth

# Redis
REDIS_URL=redis://localhost:6379

# ClickHouse
ANALYTICS_URL=http://localhost:8123
ANALYTICS_DATABASE=staging_ariadne

# Meilisearch
MEILISEARCH_ADDR=http://localhost:7700
MEILISEARCH_KEY=modrinth

# S3 (local development - optional)
# S3_URL=http://localhost:9000  # If using MinIO
# S3_BUCKET_NAME=modrinth

# Email (Mailpit)
SMTP_HOST=localhost
SMTP_PORT=1025

# Auth (for OAuth - use test credentials)
GITHUB_CLIENT_ID=your_test_client_id
GITHUB_CLIENT_SECRET=your_test_secret

# Internal API key (for admin endpoints)
ADMIN_KEY=feedbeef

Running Frontend Applications

Web Interface

# From root directory
cd apps/frontend
cp .env.local .env

# Edit .env if needed to point to local Labrinth
# NUXT_PUBLIC_LABRINTH_URL=http://localhost:8000

cd ../..
pnpm web:dev
Access at http://localhost:3000

Desktop App

# From root directory
cd packages/app-lib
cp .env.local .env

cd ../..
pnpm app:dev
The Tauri app window will open automatically.

Database Management

Resetting the Database

To start fresh:
# Stop services
docker compose down -v

# Remove all data
rm -rf docker-volumes/  # If you have local volumes

# Start services
docker compose up -d

# Run migrations
cd apps/labrinth
cargo sqlx migrate run

Seeding Test Data

Create test data for development:
-- Connect to PostgreSQL
docker exec -it labrinth-postgres psql -U labrinth -d labrinth

-- Create test user
INSERT INTO users (id, username, email, created)
VALUES ('local_1', 'testuser', '[email protected]', NOW());

-- Create test project
INSERT INTO projects (id, slug, title, description, published)
VALUES ('local_proj_1', 'test-mod', 'Test Mod', 'A test mod', NOW());
Or use the Labrinth API to create projects:
# Authenticate (requires GitHub OAuth setup)
curl -X POST http://localhost:8000/v3/project \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "title": "Test Mod",
    "slug": "test-mod",
    "description": "A test mod",
    "project_type": "mod",
    "categories": ["technology"],
    "license_id": "MIT"
  }'

Backup and Restore

Backup:
docker exec labrinth-postgres pg_dump -U labrinth labrinth > backup.sql
Restore:
cat backup.sql | docker exec -i labrinth-postgres psql -U labrinth -d labrinth

Optional Services

Delphi (Malware Scanner)

Delphi scans uploaded files for malware. It’s optional for local development.
# Start with Delphi profile
docker compose --profile with-delphi up -d
Port: 59999

Sharded Meilisearch

For testing search with multiple Meilisearch instances:
docker compose --profile sharded-meilisearch up -d
This starts:
  • meilisearch0 on 7700
  • meilisearch1 on 7701
  • Nginx load balancer on 7710

Running Labrinth in Docker

To run Labrinth itself in Docker:
docker compose --profile with-labrinth up -d
This builds and runs Labrinth in a container instead of running it locally with cargo run.

Troubleshooting

Services Won’t Start

# Check service logs
docker compose logs postgres_db
docker compose logs redis
docker compose logs clickhouse

# Check all logs
docker compose logs -f

Port Already in Use

If a port is already in use, edit docker-compose.yml to change the port mapping:
ports:
  - '127.0.0.1:5433:5432'  # Use 5433 instead of 5432
Then update DATABASE_URL in .env:
DATABASE_URL=postgres://labrinth:labrinth@localhost:5433/labrinth

Database Connection Errors

# Verify PostgreSQL is running and healthy
docker compose ps postgres_db

# Check connection
psql postgresql://labrinth:labrinth@localhost:5432/labrinth -c "SELECT 1;"

Meilisearch Index Issues

If search isn’t working:
# Check Meilisearch health
curl http://localhost:7700/health

# List indexes
curl http://localhost:7700/indexes -H "Authorization: Bearer modrinth"

# Trigger reindex from Labrinth
curl -X POST http://localhost:8000/_internal/search/reindex \
  -H "Authorization: Bearer feedbeef"

Slow Compilation (Rust)

Speed up Rust compilation:
# Install sccache (compilation cache)
cargo install sccache

# Add to ~/.cargo/config.toml
[build]
rustc-wrapper = "sccache"

# Or use mold linker (Linux)
sudo apt install mold
export RUSTFLAGS="-C link-arg=-fuse-ld=mold"

Out of Disk Space

Docker can consume a lot of disk space:
# Clean up unused containers and images
docker system prune -a

# Remove volumes
docker volume prune

Development Workflow

Typical Development Session

# 1. Start services
docker compose up -d

# 2. Start Labrinth (in one terminal)
cd apps/labrinth
cargo run

# 3. Start frontend (in another terminal)
pnpm web:dev

# 4. Make changes and test

# 5. When done, stop services
docker compose down

Hot Reloading

  • Frontend: Vite provides instant HMR
  • Backend: Rust requires manual restart after code changes
Tip: Use cargo-watch for auto-restart:
cargo install cargo-watch

# Auto-restart on file changes
cargo watch -x 'run -p labrinth'

Viewing Logs

# Docker service logs
docker compose logs -f postgres_db
docker compose logs -f meilisearch0

# Labrinth logs (if running with cargo)
# Already visible in terminal

# Frontend logs
# Already visible in terminal running pnpm web:dev

Performance Tips

PostgreSQL Tuning

For local development, you can increase performance by adjusting PostgreSQL settings:
# Edit docker-compose.yml to add environment variables:
environment:
  - POSTGRES_SHARED_BUFFERS=256MB
  - POSTGRES_WORK_MEM=16MB
  - POSTGRES_MAINTENANCE_WORK_MEM=64MB

Redis Memory Limit

redis:
  command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru

ClickHouse Memory

clickhouse:
  environment:
    - CLICKHOUSE_MAX_MEMORY_USAGE=4000000000  # 4GB

Next Steps

Backend (Labrinth)

Learn about the Rust backend architecture

Frontend (Web)

Develop the Nuxt 3 web interface

Testing

Run tests and ensure quality

Deployment

Learn about the production deployment