disclosure-bureau/infra/coolify/SHARED.md

3 KiB
Raw Blame History

Coolify — Shared services (Meilisearch + Dragonfly + Imgproxy)

These are stateless or namespaceable — one instance can serve all your projects. Saves RAM.

Meilisearch

Coolify → + New Service → Database → Meilisearch.

Field Value
Project name meilisearch-shared
Domain search.disclosure.top
Master key (generate; copy for MEILI_MASTER_KEY env in web app)
Resource limits 1 CPU, 1 GB RAM

Each project uses different index names: disclosure_pages, disclosure_entities, projeto_b_xxx — no cross-talk.

After deploy, in the web app env vars:

MEILISEARCH_URL=https://search.disclosure.top
MEILISEARCH_API_KEY=<master-key>

The web app will create + populate indexes on first deploy (see web/scripts/seed-meili.ts).

Dragonfly

Redis-compatible, drop-in, 25× faster than Redis. Coolify has no template, use a custom Docker Compose service:

# In Coolify → + New Resource → Service → Custom Docker Compose
services:
  dragonfly:
    image: docker.dragonflydb.io/dragonflydb/dragonfly:latest
    restart: unless-stopped
    ulimits:
      memlock: -1
    ports:
      - "6379:6379"
    volumes:
      - dragonfly-data:/data
    command: ["--logtostderr", "--cache_mode=true", "--maxmemory=512mb"]
    mem_limit: 600m
volumes:
  dragonfly-data:

Web app env:

REDIS_URL=redis://dragonfly:6379

(Coolify networks the services internally; you reach dragonfly:6379 from inside the network.)

Imgproxy

Stateless image resizer:

services:
  imgproxy:
    image: ghcr.io/imgproxy/imgproxy:latest
    restart: unless-stopped
    environment:
      IMGPROXY_KEY: ${IMGPROXY_KEY}
      IMGPROXY_SALT: ${IMGPROXY_SALT}
      IMGPROXY_USE_ETAG: "true"
      IMGPROXY_TTL: "31536000"
      IMGPROXY_MAX_SRC_RESOLUTION: "50"
      IMGPROXY_ENABLE_WEBP_DETECTION: "true"
      IMGPROXY_LOCAL_FILESYSTEM_ROOT: "/data"
    volumes:
      - /data/ufo/processing:/data:ro
    ports:
      - "8080:8080"
    mem_limit: 256m

Generate IMGPROXY_KEY and IMGPROXY_SALT:

openssl rand -hex 64    # → key
openssl rand -hex 64    # → salt

Add subdomain img.disclosure.top → :8080.

Web app uses signed URLs like:

https://img.disclosure.top/<signature>/rs:fit:800:0/plain/local:///png/doc-x/p-001.png

The web/lib/imgproxy.ts helper generates these signatures.

Backups (restic + Backblaze B2)

Optional but strongly recommended. Coolify has a built-in backup feature per service:

  1. Each Supabase project's stack → Backups → set schedule (e.g., 0 3 * * * daily 3am).
  2. Destination: configure B2 bucket in Coolify Settings → Backups (one-time setup).

Restic runs encrypted, deduplicated. ~$0.005/GB/mo on B2.

Done

Stack complete:

  • disclosure.top (Next.js)
  • db.disclosure.top (Supabase Kong)
  • studio.disclosure.top (Supabase Studio)
  • search.disclosure.top (Meilisearch)
  • img.disclosure.top (Imgproxy)
  • coolify.disclosure.top (Coolify panel)

Total RAM in production: ~5-6 GB. Plenty of room for 2-3 more projects.