Environments & Deployment
Where each component runs, how it ships, and the domains that front it.
Hosting at a glance
| Component | Hosting | Deploy mechanism |
|---|---|---|
| Public website (web2) | Linode (Apache/PHP), CloudFlare in front | Docker image build (GitHub Actions) + legacy git-deploy.php webhook |
| REST API | Linode, PM2 cluster behind a load balancer | PM2 deploy (ecosystem.config.js): git pull → npm install → pm2 startOrRestart |
| API worker | api-3 (Linode), managed via Coolify | Coolify builds/runs Dockerfile.worker; control plane on the coolify host |
| Admin panel | Heroku (not Linode) | Branch-based CI/CD: develop / staging / master apps; Heroku review apps per PR |
| MongoDB | Linode — 3-node replica set (mongo-1/2/3) | Managed directly on the DB servers |
Domains
Public site (prod)
*.macaronikid.com — e.g. national.macaronikid.comAPI (prod)
api.macaronikid.comAdmin (prod)
admin.macaronikid.comStatus page
status.macaronikid.comDev
*.macaronikidlab.com, api.macaronikidlab.comInternal (API nodes)
api1/api2/api3.internal.macaronikid.com, api-develop.internal.…Server inventory
Confirmed from the Linode account (June 10, 2026). All instances are in us-east. Two NodeBalancers front production: one across the API nodes, one across the web front ends.
Production API — PM2, behind a NodeBalancer
| Server | Public IPv4 | Plan | Notes |
|---|---|---|---|
api-1 | 45.33.85.145 | g6-standard-4 | PM2, multiple instances |
api-2 | 172.104.21.186 | g6-standard-4 | PM2, multiple instances |
api-4 | 45.33.73.175 | g6-dedicated-8 | PM2, multiple instances (largest API node) |
API worker / Coolify
| Server | Public IPv4 | Plan | Notes |
|---|---|---|---|
api-3 | 50.116.60.72 | g6-standard-4 | Managed by Coolify — does not run PM2 like the others; runs the API worker |
coolify | 50.116.54.22 | g6-standard-2 (Ubuntu 24.04) | Coolify control plane (self-hosted PaaS) |
api-dev | 66.228.41.190 | g6-standard-4 | Development API server |
MongoDB — 3-node replica set
| Server | Public IPv4 | Plan | Notes |
|---|---|---|---|
mongo-1 | 66.228.44.13 | g6-standard-8 | The backed-up node (preferred primary) |
mongo-2 | 104.237.144.131 | g6-standard-16 | Often acts as primary; largest RAM (64 GB); historically hottest |
mongo-3 | 66.228.36.184 | g6-standard-8 | Replica member |
mongo-dev | 173.255.231.89 | g6-standard-4 | Available for development; not wired into anything. Refreshed by spinning up a new instance from a backup (no streamlined prod→dev sync) |
Front end (web2) — behind a NodeBalancer
| Server | Public IPv4 | Plan | Notes |
|---|---|---|---|
web-front-1 | 66.228.38.183 | g6-standard-6 | Production front end |
web-front-2 | 45.33.94.22 | g6-standard-6 | Production front end |
web-front-develop | 97.107.141.152 | g6-standard-4 | Front end used only for newsletter generation, e.g. national.www3.macaronikid.com/dynamic-newsletter/[id]/[townslug] |
web-front-preview | 69.164.210.83 | g6-standard-4 | /var/www/html = Yodel-calendar preview for publishers who haven't enabled it (e.g. erie.preview.macaronikid.com/events); /var/www/dev = active development on the develop branch |
Being retired (offline as of June 10, 2026)
| Server | Public IPv4 | Notes |
|---|---|---|
analytics | 45.79.159.187 | The Matomo box — offline; being deleted (see Analytics Decommission) |
api-script | 104.237.147.163 | One-off script server — offline; being retired |
Stale deploy config
The API repo's ecosystem.config.js defines a single deploy host (198.74.62.36) that does not match any current Linode. Production API deploys actually target api-1, api-2, and api-4 (PM2 behind a NodeBalancer); api-3 is Coolify-managed. Treat the committed ecosystem.config.js host as outdated.
Edge & delivery
The CDN/proxy setup is split by domain type:
| Domains | Fronted by | Why |
|---|---|---|
Production town subdomains — national.macaronikid.com, erie.macaronikid.com, etc. | CloudFlare (proxied) | Each town is defined manually in CloudFlare to use its proxy |
*.dev.…, *.preview.…, *.www3.… | AWS CloudFront | Wildcard coverage — avoids defining every subdomain by hand |
Load balancing
Linode NodeBalancers — one across the API nodes (api-1/2/4), one across the web front ends (web-front-1/2)
Local development
The macaroni-kid-2.docker-compose repo wires the stack together for local work. docker-compose.yml builds the API (port 3000:8080), admin (3003), web (3001:80), and a local mongo:3.4 with a db-init seed step. docker-compose.prod.yml is a production-style reference (note: it still points at the older macaroni-kid-2.web rather than web2, and maps the API on port 3000 instead of 8080).
CI workflows
| Repo | Workflow | Purpose |
|---|---|---|
| web2 | .github/workflows/docker.yml | Build the Docker image |
| admin-panel | .github/workflows/docker.yml | Build the Docker image |
| api | claude.yml, claude-code-review.yml | Automated code review (no deploy step in CI; deploy via PM2) |