Merge pull request #60 from ArnasDon/fix/hostinger-managed-nodejs

fix(hostinger): recommend Managed Node.js Hosting, not VPS
This commit is contained in:
Arnas Donauskas 2026-04-19 21:03:44 +03:00 committed by GitHub
commit 9fc3eb772b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 157 additions and 159 deletions

View File

@ -13,7 +13,7 @@ docs walk you from a freshly forked repo to a production deploy.
phone number, wire the webhook.
4. **[Environment variables](./environment-variables.md)** — the full reference.
5. **[Deploy on Hostinger](./deployment-hostinger.md)** — recommended hosting
walkthrough (Node.js VPS).
walkthrough (Managed Node.js Hosting).
6. **[Automations cron](./automations-and-cron.md)** — schedule the pending
executions drain so waits and delays fire.
7. **[Troubleshooting](./troubleshooting.md)** — the usual suspects.
@ -26,8 +26,8 @@ docs walk you from a freshly forked repo to a production deploy.
WhatsApp Business app.
- A WhatsApp phone number that is **not** already tied to the regular
WhatsApp or WhatsApp Business mobile apps.
- A [Hostinger](https://www.hostinger.com/vps-hosting) VPS plan (or any Node
host that supports long-running Node.js 20+ processes).
- A [Hostinger Managed Node.js Hosting](https://www.hostinger.com/web-apps-hosting)
plan (or any Node host that runs long-running Node.js 20+ processes).
## Stack at a glance
@ -35,7 +35,7 @@ docs walk you from a freshly forked repo to a production deploy.
- **Database + auth + storage** — Supabase (Postgres + RLS).
- **WhatsApp transport** — Meta Cloud API (official WhatsApp Business API).
- **Encryption** — AES-256-CBC for per-user access tokens.
- **Scheduler** — external cron that pings `GET /api/automations/cron`.
- **Scheduler** — cron (or any pinger) that hits `GET /api/automations/cron`.
## Getting help

View File

@ -34,25 +34,17 @@ Copy the result into `AUTOMATION_CRON_SECRET` in your deployment env.
## 2. Schedule the pinger
### Option A — cron on the same VPS (Hostinger)
### Option A — hPanel cron (Hostinger Managed Node.js)
```bash
crontab -e
hPanel → **Advanced → Cron Jobs** → add a new job set to every minute
(`* * * * *`). Command:
```
curl -s -H "x-cron-secret: <AUTOMATION_CRON_SECRET>" https://crm.example.com/api/automations/cron > /dev/null
```
```cron
* * * * * curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https://crm.example.com/api/automations/cron > /dev/null
```
Export the secret into the cron environment. The simplest way is to add
it to the user's shell profile and wrap the command:
```cron
* * * * * bash -lc 'curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https://crm.example.com/api/automations/cron > /dev/null'
```
Or, for a more disciplined setup, use a `systemd` timer with
`EnvironmentFile=/etc/wacrm.env`.
Paste the literal secret — hPanel cron jobs don't inherit the Node app's
environment variables.
### Option B — external pinger

View File

@ -1,158 +1,153 @@
# Deploy on Hostinger
Hostinger's **VPS Hosting** plans are the recommended way to run WaCRM in
production: you get a dedicated Node.js process, predictable pricing, and
Hostinger's `hPanel` handles SSL and firewalls for you.
Hostinger's **[Managed Node.js Hosting](https://www.hostinger.com/web-apps-hosting)**
is the recommended way to run WaCRM in production: Hostinger patches the
OS and the Node runtime, handles SSL, and gives you a Git-based deploy
flow from hPanel. You deploy from your fork without ever opening an SSH
session if you don't want to.
This walkthrough uses a VPS running Ubuntu 24.04.
This walkthrough assumes you have already completed
[getting-started](./getting-started.md), [supabase-setup](./supabase-setup.md),
and [whatsapp-setup](./whatsapp-setup.md) — i.e., your fork builds locally
and you have your Supabase + Meta credentials ready.
## 1. Provision the VPS
## 1. Buy a Managed Node.js plan
1. Sign up at <https://www.hostinger.com/vps-hosting> and pick a plan. The
**KVM 2** tier (2 vCPU / 8 GB RAM) is a comfortable starting point.
2. During the setup wizard:
- **OS template** → Ubuntu 24.04 (plain, not the managed Node template —
we install what we need manually).
- **Location** → region closest to your users.
- **SSH key** → upload your public key (`~/.ssh/id_ed25519.pub` or
similar) so you can SSH in passwordless.
3. Wait for provisioning. In hPanel you will see the IPv4 address and
root credentials.
1. Sign up at <https://www.hostinger.com/web-apps-hosting> and choose a
Node.js plan that gives you enough memory for `npm run build` (2 GB+
recommended).
2. During the onboarding wizard, pick the **Node.js** stack and the region
closest to your users.
3. Finish setup until you land in **hPanel**.
## 2. SSH in and install dependencies
## 2. Create the app in hPanel
```bash
ssh root@<your-ip>
In hPanel, open the hosting plan and go to **Websites → Create / Manage**
and pick the **Node.js** application option, then:
# Create a non-root user for the app
adduser wacrm
usermod -aG sudo wacrm
rsync --archive --chown=wacrm:wacrm ~/.ssh /home/wacrm
- **Application name** — `wacrm`.
- **Application root** — accept the default (e.g., `domains/yourdomain/public_html`).
- **Application URL** — the domain or subdomain you want to use (e.g.,
`crm.example.com`). You can attach a domain now or later under
**Domains**.
- **Node.js version** — 20 or newer.
- **Application startup file / command** — Next.js uses `npm start` once
it is built, so set:
- Start command: `npm start`
- Alternatively: `node node_modules/next/dist/bin/next start -p $PORT`
# Switch user
su - wacrm
Save the app. hPanel provisions a container and shows you the app's
control page.
# Install Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs git
## 3. Connect your GitHub fork
# Install PM2 for process management
sudo npm install --global pm2
```
In hPanel → **Git**:
## 3. Clone your fork
1. **Create repository** → paste your fork's HTTPS URL
(e.g., `https://github.com/<your-username>/wacrm.git`).
2. Pick the branch you want to deploy (usually `main`).
3. Set the deploy path to the **Application root** from step 2.
```bash
cd ~
git clone https://github.com/<your-username>/wacrm.git
cd wacrm
npm ci
```
Alternative: if you prefer ZIP uploads, use **File Manager** instead —
upload the repo contents into the application root. Git-based deploy is
simpler because redeploying is one click.
## 4. Configure env vars
## 4. Install dependencies and build
Create `/home/wacrm/wacrm/.env.local` with the values from
[environment-variables.md](./environment-variables.md). Make sure
`NEXT_PUBLIC_SITE_URL` is set to the exact public URL you will use
(e.g., `https://crm.example.com`).
hPanel's Node.js app page exposes a **Run NPM install** button and a
terminal. Either works:
```bash
chmod 600 .env.local
```
- **Button flow**:
1. Click **Run NPM install**. Wait for it to finish.
2. Click **Run NPM Build** (or execute `npm run build` from the app
terminal).
- **Terminal flow**:
```bash
npm ci
npm run build
```
## 5. Build and start with PM2
> Next.js expects `NEXT_PUBLIC_*` variables to be present **at build
> time**, so set env vars (step 5) **before** running the build.
```bash
npm run build
pm2 start npm --name wacrm -- start
pm2 save
pm2 startup systemd -u wacrm --hp /home/wacrm
# Follow the command PM2 prints — it enables auto-start on reboot.
```
## 5. Configure environment variables
WaCRM is now listening on `127.0.0.1:3000`.
In hPanel → **Node.js app → Environment variables**, add every value from
[environment-variables.md](./environment-variables.md):
## 6. Front with nginx + SSL
- `NEXT_PUBLIC_SUPABASE_URL`
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- `SUPABASE_SERVICE_ROLE_KEY`
- `ENCRYPTION_KEY`
- `META_APP_SECRET`
- `NEXT_PUBLIC_SITE_URL` — set to `https://<your-domain>` (with the
scheme, without a trailing slash).
- `AUTOMATION_CRON_SECRET` — if you plan to use Wait steps
([automations-and-cron.md](./automations-and-cron.md)).
Point your domain's `A` record at the VPS IP. Then:
Save, then **re-run the build** so the new `NEXT_PUBLIC_*` values get
baked into the client bundle.
```bash
sudo apt-get install -y nginx certbot python3-certbot-nginx
```
## 6. Start the app
Create `/etc/nginx/sites-available/wacrm`:
From the app page, click **Restart application** (or run the equivalent
from the terminal). hPanel boots `npm start` behind its own reverse proxy
on the domain you configured. Hit the URL in a browser — you should land
on the WaCRM marketing page.
```nginx
server {
server_name crm.example.com;
SSL is provisioned automatically. If you used a subdomain, Hostinger's
AutoSSL usually takes a minute or two; until then the site may serve the
plain-HTTP version.
client_max_body_size 25m; # WhatsApp media upload ceiling
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen 80;
}
```
Enable and issue a cert:
```bash
sudo ln -s /etc/nginx/sites-available/wacrm /etc/nginx/sites-enabled/wacrm
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d crm.example.com
```
Certbot writes the 443 block, enables HTTP→HTTPS redirect, and schedules
renewals in `systemd`.
## 7. Firewall
hPanel → **Firewall**: allow inbound 22 (SSH), 80, 443. Everything else
stays closed.
## 8. Update the Meta webhook
## 7. Update the Meta webhook
Back in **Meta for Developers → WhatsApp → Configuration**, change the
callback URL to `https://crm.example.com/api/whatsapp/webhook` and re-verify.
callback URL to `https://<your-domain>/api/whatsapp/webhook` and re-verify.
## 9. Schedule the automation cron
## 8. Schedule the automations cron
Follow [automations-and-cron.md](./automations-and-cron.md) to drain
pending executions. A simple `cron` entry on the same VPS works:
If you use the Wait step in any automation, schedule the cron drain.
```bash
crontab -e
```
- **Inside hPanel** — open **Advanced → Cron Jobs** and add:
```
* * * * * curl -s -H "x-cron-secret: <AUTOMATION_CRON_SECRET>" https://<your-domain>/api/automations/cron > /dev/null
```
Paste the literal secret here (cron jobs in hPanel don't read app env
vars) or store it in a file the cron reads.
- **Outside** — any uptime monitor (UptimeRobot, Better Stack, GitHub
Actions) can hit the URL once a minute. See
[automations-and-cron.md](./automations-and-cron.md) for detail.
```cron
* * * * * curl -s -H "x-cron-secret: $AUTOMATION_CRON_SECRET" https://crm.example.com/api/automations/cron > /dev/null
```
## 9. Deploying updates
Load `AUTOMATION_CRON_SECRET` into the cron environment via
`/etc/environment` or an `EnvironmentFile=` in a `systemd` unit.
Two options:
## 10. Deploying updates
- **Click-deploy**: push to your fork, then hit **Pull** in hPanel → Git
and **Restart application**.
- **Terminal**:
```bash
cd <application-root>
git pull
npm ci
npm run build
# then restart from the Node.js app page
```
```bash
ssh wacrm@<your-ip>
cd ~/wacrm
git pull
npm ci
npm run build
pm2 reload wacrm
```
If the database schema changed, apply any new SQL files from
`supabase/migrations/` in the Supabase SQL editor first — migrations are
idempotent.
`pm2 reload` performs a zero-downtime restart. If the database schema
changed, apply any new SQL files from `supabase/migrations/` in the
Supabase SQL editor first — migrations are idempotent.
## When to reach for a VPS instead
Managed Node.js covers most WaCRM deploys. Consider
[Hostinger VPS](https://www.hostinger.com/vps-hosting) if you need:
- Long-running background workers beyond what the single Next.js process
gives you.
- System-level cron behaviour you can't replicate from hPanel.
- Custom binaries (e.g., ffmpeg) for media transforms.
Otherwise, Managed Node.js is the fast path.
## Where to go next

View File

@ -48,8 +48,9 @@ AUTOMATION_CRON_SECRET=generate-a-long-random-string
## Security checklist
- Never commit `.env.local`. The repo already ignores it.
- On Hostinger / any VPS, set env vars via the platform's **Environment**
panel rather than writing them into a tracked file on disk.
- On Hostinger Managed Node.js (and any other host), set env vars via the
platform's **Environment variables** panel rather than writing them
into a tracked file on disk.
- Rotate `SUPABASE_SERVICE_ROLE_KEY` if it leaks — Supabase lets you
regenerate it under Project Settings → API.
- Treat `ENCRYPTION_KEY` like a database master key. Losing it means

View File

@ -96,23 +96,32 @@ up. Either consolidate to one deploy or route the webhook to exactly one.
## Deploy
### `502 Bad Gateway` from nginx
### `502 Bad Gateway` or "Application not running" from Hostinger
- `pm2 status` — is the `wacrm` process up?
- `pm2 logs wacrm` — is the Node process crashing on boot (usually a
missing env var)?
- `sudo nginx -t` — is the nginx config valid?
- Open the Node.js app page in hPanel and confirm the status is **Started**.
- Check the app log viewer for a crash on boot — almost always a missing
env var. Re-save your env vars, re-run the build, and restart.
- Confirm the **Application startup command** is `npm start` (or
equivalent) and that a build has actually run (`.next/` exists in the
application root).
### Meta webhook works until the cert renews
### Env var changes don't take effect in the browser
Certbot renews in-place and reloads nginx. If you see a certificate error
right after renewal, restart nginx (`sudo systemctl restart nginx`) and
check `/var/log/letsencrypt/letsencrypt.log` for the last renew attempt.
`NEXT_PUBLIC_*` values are baked into the client bundle at build time.
After changing any of them, re-run `npm run build` (hPanel's **Run NPM
Build** button works) and restart the app.
### Meta webhook suddenly stops after a domain change
Hostinger provisions a new SSL certificate when you attach a new domain
or subdomain. Until it is issued, the webhook URL serves plain HTTP and
Meta rejects it. Wait for AutoSSL to complete (minutes), then re-verify
the webhook in Meta.
## Still stuck?
Open an issue with:
- Environment (local / Hostinger / other).
- The exact error from server logs (`pm2 logs wacrm` or Next's dev output).
- Environment (local / Hostinger Managed Node.js / other).
- The exact error from the app log viewer in hPanel or Next's dev output.
- What you were trying to do when it happened.

View File

@ -38,7 +38,7 @@ export function Footer() {
},
{ href: '#self-host', label: 'Self-host' },
{
href: 'https://www.hostinger.com/vps-hosting',
href: 'https://www.hostinger.com/web-apps-hosting',
label: 'Deploy on Hostinger',
external: true,
},

View File

@ -3,7 +3,7 @@ import { Section, SectionHeader } from './section'
import { GithubIcon } from './github-icon'
const REPO_URL = 'https://github.com/ArnasDon/wacrm'
const HOSTINGER_URL = 'https://www.hostinger.com/vps-hosting'
const HOSTINGER_URL = 'https://www.hostinger.com/web-apps-hosting'
export function OpenSource() {
return (
@ -11,7 +11,7 @@ export function OpenSource() {
<SectionHeader
eyebrow="Open source"
title="Fork it, brand it, host it"
description="WaCRM is a template you can take and make your own. Grab the source on GitHub and self-host — we recommend Hostinger for a one-click Node.js VPS."
description="WaCRM is a template you can take and make your own. Grab the source on GitHub and self-host — we recommend Hostinger Managed Node.js Hosting for a zero-ops deploy."
/>
<div className="mx-auto grid max-w-5xl grid-cols-1 gap-5 md:grid-cols-2">
@ -53,11 +53,12 @@ export function OpenSource() {
</span>
</h3>
<p className="mt-1.5 text-sm leading-relaxed text-slate-400">
Best fit for this template — spin up a Node.js VPS, point it at
your forked repo, and WaCRM is live in a few minutes.
Best fit for this template — connect your forked repo to
Managed Node.js Hosting and WaCRM is live in a few minutes. No
servers to patch.
</p>
<span className="mt-5 inline-flex items-center gap-1.5 text-sm font-medium text-emerald-400 transition-colors group-hover:text-emerald-300">
Get Hostinger VPS
Managed Node.js Hosting
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-0.5" />
</span>
</a>