Iderbrandt
HemBloggÄmnenLänkarOm

Iderbrandt.com

Personlig blogg om utveckling, teknik och det som fångar intresset — skrivet av Tobias Iderbrandt.

Utforska

BloggÄmnenLänkarOmIntegritetRSS

Konto

Logga inKaffe
© 2026 iderbrandt.com
Kaffe
Blog·Admin·8 min läsning·14 visningar

Hur jag kör node.js/next.js hos Oderland

18 juni 2026
Next.js
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

.github/workflows/deploy-oderland.yml

Bygg + rsync + npm ci + omstart

scripts/prepare-oderland-cpanel.sh

Paketerar deploy-bundle

scripts/oderland-npm-ci.sh

CloudLinux-kompatibel npm ci

scripts/setup-github-ssh.sh

SSH-nyckel i GitHub Actions

oderland/server.js

Passenger-startfil


Översikt — hur det hänger ihop

  1. Bygget sker i CI (inte på servern) — Oderland har begränsad CPU/RAM för next build.

  2. Artefakter laddas upp till app-roten via rsync (utan node_modules).

  3. npm ci körs i CloudLinux virtualenv — paket hamnar i nodevenv/.../lib/node_modules.

  4. App-roten har bara en symlink node_modules → virtualenv (krav från CloudLinux).

  5. 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 node_modules-regel

App-roten får inte innehålla en riktig node_modules-mapp. Standalone-deploy lägger ofta node_modules i roten → appen vägras starta eller npm trasas.

next start / standalone-server

Standalone vill köra node .next/standalone/server.js. Det krockar med vår custom server och virtualenv-upplägg.

Bygg på servern

next build på shared hosting är långsamt och opålitligt.

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 (node-version: '22')

Application mode

Production

Application root

/home/ffcsse/bbs2026

Inte nodevenv-mappen

Application URL

beta.borasbs.se + /

Måste matcha domänen besökare använder

Application startup file

server.js

Kopieras från oderland/server.js vid deploy

Spara och notera:

  • Application root → GitHub secret ODERLAND_APP_PATH

  • Virtualenv 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:

  1. Lokalt: node scripts/encode-google-service-account.mjs service-account.json

  2. GitHub secret: GOOGLE_SHEETS_SERVICE_ACCOUNT_JSON_B64

  3. Deploy skriver filen till .secrets/google-sheets.json

  4. cPanel 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

[oderland/server] prepare OK

Next.js-bygget är läsbart

listening on passenger

Korrekt under cPanel/Passenger

listening on ...:3000

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:

  1. Om det finns en riktig node_modules-mapp → flytta till virtualenv eller ta bort

  2. Skapa symlink node_modules → .../lib/node_modules

  3. Köra "$NPM_BIN" ci --omit=dev --legacy-peer-deps

  4. Reparera symlink om npm ersatte den med en riktig mapp igen

  5. Verifiera att node_modules fortfarande ä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_modules före upload om den finns

  • Kör oderland-npm-ci.sh efter 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

  1. Validate secrets — SSH, paths, Supabase URL

  2. npm ci + npm run build — med NEXT_PUBLIC_* från secrets

  3. prepare-oderland-cpanel.sh — bundle

  4. SSH agent — setup-github-ssh.sh laddar ODERLAND_SSH_KEY

  5. rsync .oderland-deploy/ → $ODERLAND_APP_PATH/

  6. Google credentials (valfritt) — skriver .secrets/google-sheets.json

  7. oderland-npm-ci.sh på servern

  8. Restart — touch $APP_PATH/tmp/restart.txt (Passenger/cPanel-konvention)

GitHub Secrets

Secret

Beskrivning

ODERLAND_SSH_HOST

t.ex. sshXX.oderland.com

ODERLAND_SSH_USER

cPanel-användarnamn

ODERLAND_SSH_KEY

Hela privata nyckeln (BEGIN … END)

ODERLAND_SSH_PASSPHRASE

Endast om nyckeln har lösenfras

ODERLAND_SSH_PORT

Valfritt, default 22

ODERLAND_APP_PATH

Application root

ODERLAND_NPM_BIN

Virtualenv npm-sökväg

NEXT_PUBLIC_SITE_URL

t.ex. https://beta.borasbs.se

NEXT_PUBLIC_BASE_URL

Samma som ovan

NEXT_PUBLIC_SUPABASE_URL

Supabase project URL

NEXT_PUBLIC_SUPABASE_ANON_KEY

Anon key (eller NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY / SUPABASE_ANON_KEY)

GOOGLE_SHEETS_SERVICE_ACCOUNT_JSON_B64

Valfritt

Övriga NEXT_PUBLIC_*

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

  1. cPanel → SSH Access → generera Ed25519-nyckel utan lösenfras (enklast i CI)

  2. Auktorisera den publika nyckeln (Manage → Authorize) — annars Permission denied (publickey)

  3. Klistra in privata nyckeln i ODERLAND_SSH_KEY

  4. Jä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

  1. Application URL = rätt domän, startup file = server.js, mode = Production

  2. Restart i cPanel efter deploy

  3. Läs Passenger/Node-loggen i cPanel (inte bara generell Apache error log)

  4. 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

Permission denied (publickey)

Auktorisera nyckel i cPanel, kontrollera host/user/key

Enter passphrase

Nyckel med lösenfras → ODERLAND_SSH_PASSPHRASE eller ny nyckel utan

rsync: mkdir ... Permission denied

Fel ODERLAND_APP_PATH

export: KEY-----… not a valid identifier

Trasig flerradig env i cPanel — ta bort/fixa Google JSON

CloudLinux node_modules-fel

rm -rf node_modules i app-roten, kör deploy igen

Efter kodändring syns inte på sajten

  1. Kontrollera att Actions-deploy lyckades

  2. touch ~/bbs2026/tmp/restart.txt

  3. Hå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:

  1. Rsync av output: standalone med node_modules i app-roten → CloudLinux blockerade.

  2. next start eller .next/standalone/server.js som startup → krock med virtualenv och custom server.

  3. Bygga på servern via cPanel → för långsamt/opålitligt.

  4. Domän som pekar på tom /home/.../beta.borasbs.se/ → 503, ingen träff i Passenger-loggar.

  5. Flerradig GOOGLE_SHEETS_SERVICE_ACCOUNT_JSON i cPanel → alla npm-kommandon trasiga.

  6. 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

output: standalone

Ja

Ignoreras

Process manager

Container

Passenger

Env

Coolify UI

cPanel + GitHub Secrets (build)

Deploy trigger

Egen hook på main

deploy-oderland.yml på main

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.

Gillade du inlägget?

Dela det med någon som också kan tycka det är intressant.

Share this post
X (Twitter)FacebookLinkedInEmailWhatsApp

Gillade du inlägget?

Om du vill stötta arbetet med bloggen får du gärna bjuda på en kaffe.

Bjud på en kaffe

Populärt

Why I Migrated my Next.js Blog from Vercel to Cloudflare Workers

20 visningar

Getting Wi-Fi to work on an old Macbook Pro (Ubuntu)

18 visningar

Why I Moved from Cloudflare to Hetzner VPS with Coolify

18 visningar

Relaterade inlägg

Lyxig Vardagsmiddag: Krämig Kycklingpanna med Bacon, Svamp & Rostad Potatis
5 maj 2026·Blog

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

Läs inlägg
Krämig Kasslerpanna (One Pan-ish)
5 maj 2026·Blog

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.

Läs inlägg
Svårflörtade Google
2 februari 2026·Blog

Svårflörtade Google

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

Läs inlägg