
Hur jag kör node.js/next.js hos Oderland
Node.js / Next.js på Oderland (cPanel + CloudLinux + Passenger)
Den här guiden (eller ja guide.. en massa text om hur jag fick det att funka) dokumenterar hur Borås BS-webbplatsen körs på Oderland med Setup Node.js App, Phusion Passenger och deploy via GitHub Actions. Den är skriven utifrån vad som faktiskt fungerade efter flera misslyckade försök (standalone-deploy, node_modules i app-roten, fel domänkoppling m.m.).
Ber om ursäkt om texten känns lite osammanhängande men det är egentligen mest dokumentation för mig, gör ni samma resa är det förmodligen andra problem :)
Officiell Oderland-guide: Hur kommer jag igång med Node.js?
Relaterade filer i repot:
Fil | Syfte |
|---|---|
| Bygg + rsync + |
| Paketerar deploy-bundle |
| CloudLinux-kompatibel |
| SSH-nyckel i GitHub Actions |
| Passenger-startfil |
Översikt — hur det hänger ihop
flowchart LR
subgraph ci [GitHub Actions]
build[npm run build]
bundle[prepare-oderland-cpanel.sh]
rsync[rsync till server]
npmci[oderland-npm-ci.sh]
restart[touch tmp/restart.txt]
build --> bundle --> rsync --> npmci --> restart
end
subgraph oderland [Oderland server]
cpanel[cPanel Node.js App]
passenger[Phusion Passenger]
app[server.js + .next]
venv[nodevenv virtualenv]
cpanel --> passenger
passenger --> app
app --> venv
end
ci --> oderland
browser[Webbläsare] --> passenger
Bygget sker i CI (inte på servern) — Oderland har begränsad CPU/RAM för
next build.Artefakter laddas upp till app-roten via
rsync(utannode_modules).npm cikörs i CloudLinux virtualenv — paket hamnar inodevenv/.../lib/node_modules.App-roten har bara en symlink
node_modules→ virtualenv (krav från CloudLinux).Passenger startar
server.js, som anropar Next.js och lyssnar på'passenger'(inte port 3000).
Varför inte output: standalone på Oderland?
next.config.mjs har output: 'standalone' för Coolify/Docker. Det fungerar bra där, men inte som första deploy-strategi på Oderland:
Problem | Förklaring |
|---|---|
CloudLinux | App-roten får inte innehålla en riktig |
| Standalone vill köra |
Bygg på servern |
|
Lösningen: bygg i GitHub Actions, deploya en vanlig Next-produktionsbuild (.next/ + public/ + package.json) och använd oderland/server.js som entrypoint. Standalone-outputen i .next/standalone/ ignoreras på Oderland.
Vid manuell SSH-test kan du se varningen:
"next start" does not work with "output: standalone" configuration.
Det är ofarligt — vi kör varken next start eller standalone-servern på Oderland.
cPanel — skapa Node.js-appen
1. Setup Node.js App
cPanel → Setup Node.js App → Create Application
Fält | Värde (exempel) | Kommentar |
|---|---|---|
Node.js version | 22 (eller 24) | Matcha CI ( |
Application mode | Production | |
Application root |
| Inte |
Application URL |
| Måste matcha domänen besökare använder |
Application startup file |
| Kopieras från |
Spara och notera:
Application root → GitHub secret
ODERLAND_APP_PATHVirtualenv npm (visas i cPanel), t.ex.
/home/ffcsse/nodevenv/bbs2026/22/bin/npm→ODERLAND_NPM_BIN
2. Koppla domän (vanlig 503-orsak)
Om domänen pekar på en tom statisk mapp (t.ex. /home/ffcsse/beta.borasbs.se/) får du 503 eller tom sida — Apache hittar inget index.
Rätt: domänen ska hanteras av Node.js-appen via Application URL i Setup Node.js App, inte som separat document root med tom katalog.
I Apache error log kan det se ut så här (irrelevant brus):
Index file is not available in [/home/ffcsse/beta.borasbs.se/]
Det betyder att trafik går till fel plats. Fixa Application URL + Restart i cPanel.
3. Miljövariabler i cPanel (runtime)
Sätt en rad per variabel — inga radbrytningar i värdet.
Minst för denna app:
NODE_ENV=production
NEXT_PUBLIC_SITE_URL=https://beta.borasbs.se
NEXT_PUBLIC_BASE_URL=https://beta.borasbs.se
NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... # eller NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
DATABASE_URL=postgresql://...
SUPABASE_SERVICE_ROLE_KEY=...
Alternativt utan NEXT_PUBLIC_-prefix (servern läser båda vid runtime):
SUPABASE_URL=https://xxxx.supabase.co
SUPABASE_ANON_KEY=eyJ...
Inloggning kräver Supabase URL + anon/publishable key. Om de saknas vid build eller runtime visas Inloggning är inte tillgänglig. Login-sidan läser config från serverns runtime-env (cPanel), men sätt även GitHub Secrets och deploya om för övriga klientdelar.
Supabase Dashboard → Authentication → URL Configuration → lägg till https://beta.borasbs.se/** under Redirect URLs.
NEXT_PUBLIC_* bör finnas som GitHub Secrets vid build — de bakas in i övriga klientbundlar.
Google Sheets — vanlig fälla
Lägg inte flerradig JSON i cPanel som GOOGLE_SHEETS_SERVICE_ACCOUNT_JSON. Det ger:
export: KEY-----… not a valid identifier
…och då fungerar inga npm-kommandon (varken i cPanel eller deploy).
Rekommenderat:
Lokalt:
node scripts/encode-google-service-account.mjs service-account.jsonGitHub secret:
GOOGLE_SHEETS_SERVICE_ACCOUNT_JSON_B64Deploy skriver filen till
.secrets/google-sheets.jsoncPanel endast:
GOOGLE_SHEETS_SERVICE_ACCOUNT_PATH=/home/ffcsse/bbs2026/.secrets/google-sheets.json
Andra hemligheter med radbrytningar (t.ex. VAPID_PRIVATE_KEY) måste vara en rad med \n som escape, inte riktiga radbrytningar.
oderland/server.js — Passenger-startfil
Passenger förväntar sig att appen anropar listen('passenger'), inte en fast port.
if (typeof PhusionPassenger !== 'undefined') {
PhusionPassenger.configure({ autoInstall: false })
server.listen('passenger', () => {
console.error('[oderland/server] listening on passenger')
})
} else {
// Endast vid manuell SSH-test utan Passenger
server.listen(port, hostname, ...)
}
Loggrad | Betydelse |
|---|---|
| Next.js-bygget är läsbart |
| Korrekt under cPanel/Passenger |
| Manuell SSH-körning — normalt, nås inte från webben |
Startfilen använder next({ dev: false, dir: __dirname }) — alltså inte next start.
Deploy-bundle — vad som hamnar på servern
scripts/prepare-oderland-cpanel.sh skapar .oderland-deploy/:
.oderland-deploy/
├── server.js ← från oderland/server.js
├── package.json
├── package-lock.json
├── next.config.mjs
├── .next/ ← produktionsbuild
└── public/
Medvetet utelämnat: node_modules, källkod (src/), standalone-mappen som helhet.
Lokalt test:
npm run build
bash scripts/prepare-oderland-cpanel.sh
ls -la .oderland-deploy/
CloudLinux och node_modules
CloudLinux Node.js Selector kräver:
/home/ffcsse/bbs2026/node_modules → symlink → /home/ffcsse/nodevenv/bbs2026/22/lib/node_modules
Inte en vanlig katalog i app-roten.
scripts/oderland-npm-ci.sh gör:
Om det finns en riktig
node_modules-mapp → flytta till virtualenv eller ta bortSkapa symlink
node_modules→.../lib/node_modulesKöra
"$NPM_BIN" ci --omit=dev --legacy-peer-depsReparera symlink om npm ersatte den med en riktig mapp igen
Verifiera att
node_modulesfortfarande är en symlink
Kontroll på servern:
cd /home/ffcsse/bbs2026
ls -la node_modules
# ska visa: node_modules -> /home/ffcsse/nodevenv/bbs2026/22/lib/node_modules
Deploy-workflowen:
Rsync exkluderar
node_modules(--exclude node_modules)Tar bort legacy riktig
node_modulesföre upload om den finnsKör
oderland-npm-ci.shefter upload
GitHub Actions — deploy-pipeline
Trigger: push till main + manuell workflow_dispatch.
Concurrency: deploy-oderland (avbryter pågående deploy vid ny push).
Steg för steg
Validate secrets — SSH, paths, Supabase URL
npm ci + npm run build — med
NEXT_PUBLIC_*från secretsprepare-oderland-cpanel.sh — bundle
SSH agent —
setup-github-ssh.shladdarODERLAND_SSH_KEYrsync
.oderland-deploy/→$ODERLAND_APP_PATH/Google credentials (valfritt) — skriver
.secrets/google-sheets.jsonoderland-npm-ci.sh på servern
Restart —
touch $APP_PATH/tmp/restart.txt(Passenger/cPanel-konvention)
GitHub Secrets
Secret | Beskrivning |
|---|---|
| t.ex. |
| cPanel-användarnamn |
| Hela privata nyckeln (BEGIN … END) |
| Endast om nyckeln har lösenfras |
| Valfritt, default 22 |
| Application root |
| Virtualenv npm-sökväg |
| t.ex. |
| Samma som ovan |
| Supabase project URL |
| Anon key (eller |
| Valfritt |
Övriga | Cloudinary, Umami, VAPID, m.m. |
Runtime-hemligheter (DATABASE_URL, SUPABASE_SERVICE_ROLE_KEY, CRON_SECRET, …) sätts i cPanel, inte nödvändigtvis i Actions (utom om de behövs vid build).
SSH-nyckel för deploy
cPanel → SSH Access → generera Ed25519-nyckel utan lösenfras (enklast i CI)
Auktorisera den publika nyckeln (Manage → Authorize) — annars
Permission denied (publickey)Klistra in privata nyckeln i
ODERLAND_SSH_KEYJämför fingerprint i Actions-loggen (Start SSH agent) med cPanel
Test lokalt:
ssh -i deploy_key -o IdentitiesOnly=yes användare@sshXX.oderland.com
Felsökning
503 Service Unavailable
Application URL = rätt domän, startup file =
server.js, mode = ProductionRestart i cPanel efter deploy
Läs Passenger/Node-loggen i cPanel (inte bara generell Apache error log)
SSH-test:
cd /home/ffcsse/bbs2026
/home/ffcsse/nodevenv/bbs2026/22/bin/node server.js
Förväntat utan Passenger: prepare OK + listening on ...:3000.
Deploy / SSH
Symptom | Åtgärd |
|---|---|
| Auktorisera nyckel i cPanel, kontrollera host/user/key |
| Nyckel med lösenfras → |
| Fel |
| Trasig flerradig env i cPanel — ta bort/fixa Google JSON |
CloudLinux |
|
Efter kodändring syns inte på sajten
Kontrollera att Actions-deploy lyckades
touch ~/bbs2026/tmp/restart.txtHård refresh i webbläsaren (service worker kan cacha på vissa sidor)
Manuell omstart
touch /home/ffcsse/bbs2026/tmp/restart.txt
Eller Restart i cPanel → Setup Node.js App.
Vad vi provade som inte funkade
Dokumenterat så ni slipper göra om misstagen:
Rsync av
output: standalonemednode_modulesi app-roten → CloudLinux blockerade.next starteller.next/standalone/server.jssom startup → krock med virtualenv och custom server.Bygga på servern via cPanel → för långsamt/opålitligt.
Domän som pekar på tom
/home/.../beta.borasbs.se/→ 503, ingen träff i Passenger-loggar.Flerradig
GOOGLE_SHEETS_SERVICE_ACCOUNT_JSONi cPanel → alla npm-kommandon trasiga.listen(3000)utan Passenger-check → fungerar i SSH men inte bakom Apache/Passenger.
Coolify vs Oderland
Coolify (primär) | Oderland (beta/test) | |
|---|---|---|
Build | Docker/Nixpacks | GitHub Actions |
| Ja | Ignoreras |
Process manager | Container | Passenger |
Env | Coolify UI | cPanel + GitHub Secrets (build) |
Deploy trigger | Egen hook på |
|
Båda kan deploya från samma main-branch utan att störa varandra — separata miljöer och secrets.
Snabbreferens — ny deploy
# Lokalt (valfritt — verifiera bundle)
npm run build && bash scripts/prepare-oderland-cpanel.sh
# Produktion
git push origin main # triggar Deploy to Oderland automatiskt
Eller: GitHub → Actions → Deploy to Oderland → Run workflow.
Enjoyed this post?
Share it with others who might find it helpful!
Enjoyed this post?
If this content helped you, consider buying me a coffee to support my work!
Buy me a coffeeRelated Posts

Lyxig Vardagsmiddag: Krämig Kycklingpanna med Bacon, Svamp & Rostad Potatis

Krämig Kasslerpanna (One Pan-ish)
Sugen på något lyxigt men har ont om tid? Den här krämiga kasslerpannan med pasta svänger du ihop på under 20 minuter! En perfekt vardagsräddare där rökt kassler möter en silkeslen gräddsås med tomat och vitlök. Enkel matlagning när den är som allra bäst.

Svårflörtade Google
Få Google att godkänna bloggen för annonser och bygga med magnetiska byggklossar