mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Merge pull request #60 from ArnasDon/fix/hostinger-managed-nodejs
fix(hostinger): recommend Managed Node.js Hosting, not VPS
This commit is contained in:
commit
9fc3eb772b
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user