stashDocumentation

Self-Hosting

Run the full Stash stack on your own infrastructure in under ten minutes. One Docker Compose file covers everything.

Prerequisites

Docker + Compose24+
Python3.12+ (CLI only)
Node.js20+ (frontend dev only)

1. Clone and configure

git clone https://github.com/Fergana-Labs/stash.git
cd stash

# Copy the env template and fill in your values
cp .env.example .env

At minimum set POSTGRES_USER, POSTGRES_PASSWORD, PUBLIC_URL (your domain), and CORS_ORIGINS. Embeddings default to local sentence-transformers — no API key required. Set OPENAI_API_KEY or HF_TOKEN to use a hosted embedding provider instead. The backend itself makes no LLM calls, so there are no other keys to configure.

Edit Caddyfile and replace app.example.com with your actual domain. Caddy handles TLS automatically via Let's Encrypt — no certificate management needed.

2. Start everything

docker compose -f docker-compose.prod.yml up -d

This starts four containers:

postgres:5432PostgreSQL 16 with pgvector — stores all workspace data
backend:3456FastAPI backend
frontend:3457Next.js UI — dashboard, docs
caddy:80/443Reverse proxy with automatic HTTPS via Let's Encrypt

Alembic migrations run automatically on backend startup. Visit http://localhost:3457 to open the UI.

Environment variables

ParameterTypeDescription
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB*stringPostgres credentials. Defaults are stash/stash/stash — change before going to production.
DATABASE_URLstringFull PostgreSQL connection string. docker-compose.prod.yml auto-builds this from POSTGRES_* — only override if pointing at an external database.
PUBLIC_URLstringFrontend origin. Used in invite links and CORS config. Default: http://localhost:3457
CORS_ORIGINSstringComma-separated allowed origins. Default: http://localhost:3457,http://localhost:3456
PORTnumberBackend port. Default: 3456
DB_POOL_MIN / DB_POOL_MAXnumberDatabase connection pool size. Raise DB_POOL_MAX for high-traffic deployments.
EMBEDDING_PROVIDERstringopenai | huggingface | local | auto. Default: auto (detects from keys; falls back to local sentence-transformers if none set).
OPENAI_API_KEYstringOptional. Enables the OpenAI-compatible embedding provider (also works with any OpenAI-compatible endpoint via EMBEDDING_API_URL).
HF_TOKENstringOptional. Enables the Hugging Face Inference API embedding provider.
EMBEDDING_MODELstringOverride the embedding model name. Defaults depend on provider (text-embedding-3-small, BAAI/bge-small-en-v1.5, all-MiniLM-L6-v2).
S3_ENDPOINTstringS3-compatible endpoint for file uploads (AWS, Cloudflare R2, MinIO). Leave blank to disable.
S3_BUCKETstringS3 bucket name.
S3_ACCESS_KEY / S3_SECRET_KEYstringS3 credentials.

Optional: file storage

Stash uses any S3-compatible store for file uploads (images, PDFs, attachments). Set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, and S3_SECRET_KEY in your .env. MinIO works well for fully local deployments:

# Add to docker-compose.yml services:
minio:
  image: minio/minio
  ports:
    - "9000:9000"
    - "9001:9001"
  environment:
    MINIO_ROOT_USER: stash
    MINIO_ROOT_PASSWORD: stashdev
  command: server /data --console-address ":9001"
  volumes:
    - minio_data:/data

Then in .env:

S3_ENDPOINT=http://localhost:9000
S3_BUCKET=stash
S3_ACCESS_KEY=stash
S3_SECRET_KEY=stashdev
S3_REGION=us-east-1

Production checklist

Change default Postgres credentials

Set POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB in your .env before first run. Docker Compose and DATABASE_URL both pick them up automatically.

Configure CORS_ORIGINS

Set to your production frontend domain(s) only.

Set PUBLIC_URL

Set to your production frontend URL so invite links and share links resolve correctly.

Point Caddy at your domain

Edit Caddyfile: replace app.example.com with your real domain. Caddy auto-provisions Let's Encrypt certificates on first start.

Tune DB_POOL_MAX

Raise to 50–100 for production load. Ensure your Postgres max_connections is higher.

External Postgres

For production, use a managed database (RDS, Supabase) with pgvector enabled. Remove the postgres service from docker-compose.prod.yml and set DATABASE_URL directly.

Upgrading

git pull
docker compose build
docker compose up -d
# Migrations run automatically on backend startup

Running tests

# Backend — requires a separate test database
docker compose up -d postgres
psql postgresql://stash:stash@localhost:5432/postgres -c "CREATE DATABASE stash_test;"
DATABASE_URL=postgresql://stash:stash@localhost:5432/stash_test \
  python -m alembic upgrade head
DATABASE_URL=postgresql://stash:stash@localhost:5432/stash_test \
TEST_DATABASE_URL=postgresql://stash:stash@localhost:5432/stash_test \
  python -m pytest backend/tests/ -v

# Frontend
cd frontend && npm test