Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/midudev/mundial-de-clicks/llms.txt

Use this file to discover all available pages before exploring further.

Coolify is the recommended deployment platform for Mundial de Clicks. The stack is composed of three independent services — the Astro SSR app, a DragonFly database, and an optional Umami analytics instance — wired together exclusively through environment variables. No code changes are required to move between environments.
1
Add DragonFly
2
In your Coolify dashboard, click + New → Database → DragonFly. Coolify will provision a DragonFly container and assign it an internal service name (e.g. dragonfly). That name becomes the hostname in your REDIS_URL.
3
Write down the internal service name before continuing. You will need it in the next step when setting REDIS_URL=redis://<service-name>:6379.
4
Deploy the app
5
Click + New → Public Repository and paste https://github.com/midudev/mundial-de-clicks. Coolify will detect the Dockerfile automatically and use it to build the image.
6
Set the following environment variables on the app service:
7
VariableExample valueRequiredREDIS_URLredis://dragonfly:6379✅ YesCAP_API_URLhttps://cap.example.com/site-key✅ Yes (captcha)HOST0.0.0.0✅ YesPORT4321RecommendedORIGIN_GUARD_SECRET<long-random-string>RecommendedVOTER_ID_SECRET<long-random-string>RecommendedRATE_LIMIT_MAX5OptionalCAP_VOTES_PER_SESSION50OptionalDAILY_VOTE_MAX_PER_IP2000OptionalSTREAM_INTERVAL_MS1000OptionalMAX_SSE_CONNECTIONS_PER_IP4Optional
8
VOTER_ID_SECRET is used to sign the persistent voter_id cookie that tracks daily vote limits per browser. Keep this value stable across redeploys — changing it resets every visitor’s daily limit counter.
9
Configure the health check
10
Point Coolify’s health check to GET /api/health. This endpoint returns 200 OK with the body OK and touches no external dependencies (no DragonFly call, no database query). Coolify uses it to decide when the Node server is alive and ready to receive traffic.
11
GET /api/health
→ 200 OK  (text/plain, cache-control: no-store)
12
(Optional) Add Umami analytics
13
Click + New → Service → Umami to deploy the one-click Umami template. Once it starts, log in with the default credentials (admin / umami) — change the password immediately — create a website entry, and copy the Website ID and the script URL.
14
Add both values to the app service as build arguments (not runtime env vars):
15
Build argExample valuePUBLIC_UMAMI_SCRIPT_URLhttps://umami.example.com/script.jsPUBLIC_UMAMI_WEBSITE_IDxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
16
PUBLIC_UMAMI_SCRIPT_URL and PUBLIC_UMAMI_WEBSITE_ID are baked into the JavaScript bundle at build time by Vite. Adding or changing them requires you to trigger a full redeploy of the app service — a restart alone is not enough.
17
After saving the build args, click Redeploy on the app service to pick them up.

Full environment variable reference

The table below covers every variable understood by the app. In local development all of them are optional — defaults point to localhost:6379. In Coolify, set the ones marked Required or Recommended at minimum.
VariableDefaultRequiredDescription
REDIS_URLredis://localhost:6379✅ YesFull DragonFly connection URL. Takes priority over individual DRAGONFLY_* vars.
DRAGONFLY_HOSTlocalhostNoIgnored when REDIS_URL is set.
DRAGONFLY_PORT6379NoIgnored when REDIS_URL is set.
DRAGONFLY_PASSWORDNoIgnored when REDIS_URL is set.
REDIS_COMMAND_TIMEOUT_MS2000NoHard timeout per DragonFly command in ms. Fail-fast guard.
HOST0.0.0.0✅ YesBind address for the Node server. Must be 0.0.0.0 in containers.
PORT4321RecommendedPort the Node server listens on.
ORIGIN_GUARD_SECRETRecommendedIf set, every non-health request must carry x-origin-guard: <value>. Configure via Cloudflare Transform Rule.
VOTER_ID_SECRETRecommendedHMAC secret for signing the persistent voter_id cookie. Falls back to ORIGIN_GUARD_SECRET.
CAP_API_URL✅ YesCap standalone server URL including site key path. Captcha is disabled when empty.
CAP_VOTES_PER_SESSION50NoVotes allowed per solved captcha session before a new challenge is required.
CAP_SESSION_HARD_VOTE_CAP50NoHard ceiling on votes per Cap session regardless of CAP_VOTES_PER_SESSION.
CAP_SESSION_TTL_SECONDS120NoMaximum lifetime of a verified captcha session in seconds.
CAP_CHALLENGE_MAX_PER_MINUTE6NoMaximum Cap challenge requests per IP per minute.
CAP_REDEEM_MAX_PER_MINUTE12NoMaximum Cap redeem requests per IP per minute.
CAP_CHALLENGE_DIFFICULTY_BASE4NoBase PoW difficulty for Cap challenges.
CAP_CHALLENGE_DIFFICULTY_MAX8NoMaximum PoW difficulty for Cap challenges.
CAP_CHALLENGE_DIFFICULTY_STEP_VOTES250NoDaily votes per IP at which difficulty increases by one step.
CAP_HTTP_TIMEOUT_MS5000NoTimeout in ms for server-to-server requests to the Cap server.
RATE_LIMIT_MAX5NoMaximum valid votes per IP per window.
RATE_LIMIT_WINDOW1NoRate-limit window size in seconds.
DAILY_VOTE_MAX_PER_IP2000NoDaily vote ceiling per IP (UTC day). Votes beyond this are silently dropped.
STREAM_INTERVAL_MS1000NoMilliseconds between SSE ranking snapshots pushed to clients.
MAX_SSE_CONNECTIONS20000NoGlobal ceiling on concurrent SSE connections.
MAX_SSE_CONNECTIONS_PER_IP4NoPer-IP ceiling on concurrent SSE connections.
RANKING_MIN_REFRESH_MS750NoMinimum age in ms between forced refreshes via /api/ranking.
PUBLIC_UMAMI_SCRIPT_URLNoUmami tracking script URL. Build-time arg — requires redeploy on change.
PUBLIC_UMAMI_WEBSITE_IDNoUmami website UUID. Build-time arg — requires redeploy on change.

Production security tips

Restrict origin to Cloudflare IPs

Configure your VPS firewall (or Coolify’s port rules) to accept inbound HTTP(S) traffic only from Cloudflare’s published IP ranges. This prevents attackers from bypassing Cloudflare’s DDoS protection and rate-limiting by hitting the origin directly.

Use x-origin-guard

Set ORIGIN_GUARD_SECRET to a long random value, then create a Cloudflare Transform Rule (Modify Request Header) that injects x-origin-guard: <your-secret> on every request to your origin. The app rejects any request that arrives without this header.

Use CF-Connecting-IP for real IPs

In production, the app reads the client IP from the CF-Connecting-IP header injected by Cloudflare. Ensure your Coolify proxy forwards this header and that the origin is not reachable without it. X-Forwarded-For is not used as a trusted IP source.

Deploy Cap as a separate service

Use the Cap standalone Docker image as an additional Coolify service and set CAP_API_URL to its internal URL plus the site key path. Keeping Cap on the same private network avoids public CORS concerns and reduces latency.

Build docs developers (and LLMs) love