|
| 1 | +# Hatchbox deployment guide |
| 2 | + |
| 3 | +Quick reference for deploying an app generated from this template to Hatchbox. Steps are in the order you'd do them in the Hatchbox UI. |
| 4 | + |
| 5 | +## 1. Spin up a cluster (server) |
| 6 | + |
| 7 | +Create a new cluster in Hatchbox — typically on Digital Ocean. The cluster needs these roles: |
| 8 | + |
| 9 | +- **Web** — Rails app + the SSR Node process |
| 10 | +- **Worker** — Solid Queue background jobs (`bin/jobs`) |
| 11 | +- **Cron** — scheduled jobs (only needed if you add any; safe to include by default) |
| 12 | +- **PostgreSQL** — the single database that backs Active Record, Solid Queue, Solid Cache, and Solid Cable |
| 13 | + |
| 14 | +A single-server cluster with all roles is fine for a small app. Split them later if you outgrow it. |
| 15 | + |
| 16 | +Make sure the server image has compatible Ruby and Node versions — this app expects: |
| 17 | + |
| 18 | +- Ruby `3.3.6` (from `.ruby-version`) |
| 19 | +- Node `22.12.0` (from `.nvmrc`) |
| 20 | + |
| 21 | +## 2. Create the app |
| 22 | + |
| 23 | +- Hatchbox dashboard → New App |
| 24 | +- Connect your GitHub repo and pick the branch to deploy (e.g. `main` or `staging`) |
| 25 | +- (Optional) Enable auto-deploy on push for that branch |
| 26 | + |
| 27 | +## 3. Set environment variables |
| 28 | + |
| 29 | +App → **Environment**. At minimum: |
| 30 | + |
| 31 | +| Variable | Value | Notes | |
| 32 | +| --- | --- | --- | |
| 33 | +| `RAILS_MASTER_KEY` | Contents of `config/master.key` (or `config/credentials/production.key` if you've set up environment-scoped credentials) | Required. Do **not** commit the key file. | |
| 34 | +| `APP_HOST` | `https://yourdomain.com` | Used by `config/sitemap.rb` to build absolute URLs in `public/sitemap.xml`. Without it, the sitemap falls back to `https://example.com`. | |
| 35 | + |
| 36 | +Optional / situational: |
| 37 | + |
| 38 | +| Variable | Value | When to set it | |
| 39 | +| --- | --- | --- | |
| 40 | +| `INERTIA_SSR` | `1` or `0` | SSR is **on by default in production** (see `config/initializers/inertia_rails.rb`). Set `0` to force-disable, e.g. for debugging. | |
| 41 | +| `INERTIA_SSR_URL` | `http://localhost:13714` | Only needed if you move the SSR process to a non-default port. | |
| 42 | +| `DATABASE_URL` | postgres URL | Hatchbox usually sets this automatically when you attach the database (step 4). Only override if you're pointing at an external database. | |
| 43 | + |
| 44 | +## 4. Create the database |
| 45 | + |
| 46 | +App → **Databases** → click the button to create a PostgreSQL database for this app. Hatchbox provisions it on the PostgreSQL-role server and wires `DATABASE_URL` into the app automatically. |
| 47 | + |
| 48 | +This single database is also used by Solid Queue, Solid Cache, and Solid Cable — no separate databases needed (see `CLAUDE.md`). |
| 49 | + |
| 50 | +## 5. Set up the domain |
| 51 | + |
| 52 | +App → **Domains & SSL**: |
| 53 | + |
| 54 | +- Add your domain (e.g. `yourdomain.com` and/or `www.yourdomain.com`) |
| 55 | +- Point DNS at the Hatchbox server IP (A record for apex, CNAME for `www`) |
| 56 | +- Let Hatchbox auto-provision Let's Encrypt SSL once DNS propagates |
| 57 | + |
| 58 | +## 6. Create the SSR process |
| 59 | + |
| 60 | +App → **Processes** → Add Process. This runs the long-lived Node server that handles Inertia SSR requests from Rails. |
| 61 | + |
| 62 | +| Field | Value | |
| 63 | +| --- | --- | |
| 64 | +| Runs on | **web** | |
| 65 | +| Process name | `ssr` | |
| 66 | +| Start command | `bin/vite ssr` | |
| 67 | +| Reload command | *(empty)* | |
| 68 | +| Stop command | *(empty)* | |
| 69 | +| Restart this process on every deploy | ✅ checked | |
| 70 | +| Socket activation | unchecked | |
| 71 | + |
| 72 | +**Why this matters:** the Inertia Rails renderer POSTs page renders to this Node process at `http://localhost:13714`. If the process isn't running, Inertia silently falls back to client-only rendering — crawlers (Google, GPTBot, ClaudeBot, etc.) see an empty `<div id="app">` on public pages. |
| 73 | + |
| 74 | +The SSR bundle itself (`public/vite-ssr/ssr.js`) is built automatically during `assets:precompile` because `config/vite.json` has `"ssrBuildEnabled": true`. No extra build step needed. |
| 75 | + |
| 76 | +## 7. Create the jobs process |
| 77 | + |
| 78 | +App → **Processes** → Add Process. This runs Solid Queue. |
| 79 | + |
| 80 | +| Field | Value | |
| 81 | +| --- | --- | |
| 82 | +| Runs on | **worker** | |
| 83 | +| Process name | `jobs` | |
| 84 | +| Start command | `bin/jobs` | |
| 85 | +| Restart this process on every deploy | ✅ checked | |
| 86 | + |
| 87 | +## 8. Deploy |
| 88 | + |
| 89 | +App → **Deploy**. Hatchbox will: |
| 90 | + |
| 91 | +1. Pull the latest commit |
| 92 | +2. Run `bundle install` and `npm install` (or `bun install`) |
| 93 | +3. Run `assets:precompile` — produces both `public/vite/` (client) and `public/vite-ssr/ssr.js` (SSR) |
| 94 | +4. Run `db:prepare` (migrations + seeds on first deploy) |
| 95 | +5. Boot the web process + restart the `ssr` and `jobs` processes |
| 96 | + |
| 97 | +## Post-deploy checks |
| 98 | + |
| 99 | +- Visit the site and **view source** on a public page — `<div id="app">` should contain rendered HTML, not be empty. If it's empty, SSR isn't reaching the Node process (check the `ssr` process logs in Hatchbox). |
| 100 | +- Visit `/sitemap.xml` (after deploy + sitemap regen) — URLs should use your real domain, not `example.com`. If they don't, `APP_HOST` isn't set. |
| 101 | +- Update `public/robots.txt` so the `Sitemap:` line points at your real domain (it ships pointing at `https://example.com/sitemap.xml`). |
| 102 | +- Trigger a background job to confirm Solid Queue is processing — check the `jobs` process logs. |
| 103 | + |
| 104 | +## Troubleshooting |
| 105 | + |
| 106 | +**Blank page / empty `<div id="app">` in view source.** SSR Node process isn't running or isn't reachable. Check the `ssr` process logs. Confirm `public/vite-ssr/ssr.js` exists in the deployed release (it should, after the `ssrBuildEnabled` fix). |
| 107 | + |
| 108 | +**`ActiveRecord::ConnectionNotEstablished` or queue/cache errors.** The single PostgreSQL DB powers all four (Active Record + Solid Queue/Cache/Cable). Make sure `DATABASE_URL` is set and the DB was provisioned in step 4. |
| 109 | + |
| 110 | +**`Missing secret_key_base` or credentials errors.** `RAILS_MASTER_KEY` not set or doesn't match the encrypted credentials file in the repo. |
| 111 | + |
| 112 | +**Sitemap shows `example.com` URLs.** `APP_HOST` env var not set. |
0 commit comments