114 lines
3 KiB
Markdown
114 lines
3 KiB
Markdown
|
|
# 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:
|
|||
|
|
```env
|
|||
|
|
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:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# 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:
|
|||
|
|
```env
|
|||
|
|
REDIS_URL=redis://dragonfly:6379
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
(Coolify networks the services internally; you reach `dragonfly:6379` from inside the network.)
|
|||
|
|
|
|||
|
|
## Imgproxy
|
|||
|
|
|
|||
|
|
Stateless image resizer:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
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`:
|
|||
|
|
```bash
|
|||
|
|
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.
|