Skip to main content

Deployment Guide

Bilko Deployment Guide

Last Updated: 2026-04-16
Current State: Stable Cloud Run deployment with custom domain provisioning

GCP Project Configuration

  • Project ID: tribal-sign-487920-k0
  • Region: europe-north1 (Stockholm)
  • Services:
    • bilko-api → https://bilko-api-dh4m46blja-lz.a.run.app (revision 00037)
    • bilko-web → https://bilko-web-dh4m46blja-lz.a.run.app

Secret Manager

Secret NameVersionPurpose
bilko-cors-originsv2Comma-separated list of allowed CORS origins
bilko-database-urllatestCloud SQL connection string (password reset 2026-04-16)
bilko-jwt-refresh-secretlatestJWT refresh token secret

CORS Parsing: Secret bilko-cors-origins is parsed by comma in apps/api/src/app.ts:61

Environment Variables

bilko-web

  • NEXT_PUBLIC_API_URL=https://bilko-api-dh4m46blja-lz.a.run.app

bilko-api

  • CORS_ORIGINS → pulled from bilko-cors-origins:latest
  • SESSION_COOKIE_SECURE=true
  • NODE_ENV=production
  • DATABASE_URL → pulled from bilko-database-url:latest

Custom Domain Setup

Current Domain

  • Host: bilko-demo.alai.no
  • Mapped to: bilko-web Cloud Run service
  • DNS Provider: one.com
  • DNS Record: CNAME bilko-demo.alai.no → ghs.googlehosted.com.
  • TLS Cert: Let's Encrypt (managed by GCP, auto-renews)
  • Provisioning Time: 15-30 minutes after DNS propagation

Domain Verification Constraint

Critical: Only alai.no is verified in GCP Search Console (via [email protected]).
basicconsulting.no is NOT verified. All custom domains MUST use *.alai.no subdomains until basicconsulting.no is verified.

Custom Domain Runbook

Prerequisites

  1. Domain must be verified in Google Search Console by the GCP account owner
  2. DNS provider access (one.com for alai.no, Vercel for basicconsulting.no)
  3. gcloud CLI authenticated: gcloud auth login

Step-by-Step

1. Create Domain Mapping

gcloud beta run domain-mappings create \
  --service=bilko-web \
  --domain=bilko-demo.alai.no \
  --region=europe-north1 \
  --project=tribal-sign-487920-k0

2. Configure DNS

Add CNAME record at DNS provider:

Type: CNAME
Host: bilko-demo
Value: ghs.googlehosted.com.
TTL: 3600

3. Wait for Certificate Provisioning

gcloud beta run domain-mappings describe bilko-demo.alai.no \
  --region=europe-north1 \
  --project=tribal-sign-487920-k0

Look for status.conditions → CertificateProvisioned: True

4. Update CORS Allowed Origins

# Get current value
gcloud secrets versions access latest --secret=bilko-cors-origins

# Add new domain
echo "https://bilko-demo.alai.no,https://bilko-web-dh4m46blja-lz.a.run.app" | \
  gcloud secrets versions add bilko-cors-origins --data-file=-

5. Deploy New Revision

gcloud run services update-traffic bilko-api \
  --to-latest \
  --region=europe-north1 \
  --project=tribal-sign-487920-k0

6. Verify CORS Preflight

curl -sSI -X OPTIONS \
  https://bilko-api-dh4m46blja-lz.a.run.app/api/v1/auth/login \
  -H "Origin: https://bilko-demo.alai.no" \
  -H "Access-Control-Request-Method: POST"

Expected: HTTP 204 with Access-Control-Allow-Origin: https://bilko-demo.alai.no

GitHub Actions CI/CD

  • Workflow: .github/workflows/deploy-production.yml
  • Auth: Workload Identity Federation (WIF)
  • Service Account: [email protected]
  • IAM Roles: roles/run.admin, roles/iam.serviceAccountUser

Deployment Steps

  1. Authenticate via WIF
  2. Build Docker images (api + web)
  3. Push to Google Container Registry
  4. Deploy to Cloud Run (europe-north1)
  5. Run smoke tests (Playwright E2E)

Testing

Backend Tests

  • Framework: Vitest
  • Location: apps/api/src/**/*.test.ts
  • Command: pnpm test (from apps/api/)

End-to-End Tests

  • Framework: Playwright
  • Location: apps/e2e/tests/
  • Command: pnpm test (from apps/e2e/)
  • Runs in CI: Yes (on every deploy)

Recent Fixes (2026-04-16)

Commits

  • a62b7f6 — Cloud Run service names align: bilko-staging-*bilko-*
  • 9b1ced1 — Backend: added currency field to invoice list, added /api/v1/settings/profile endpoint
  • 73693d4 — Frontend: avatar initials fallback, invoice step indicator, chat widget ARIA labels

Key Changes

  1. Service Naming: Production services now named bilko-api and bilko-web (no -staging suffix)
  2. API Enhancements: Invoice list now includes currency, new profile settings endpoint
  3. Frontend Fixes: Accessibility improvements (ARIA), avatar initials when no image, visual polish

Rollback Procedure

Rollback to Previous Revision

# List revisions
gcloud run revisions list --service=bilko-api --region=europe-north1

# Rollback
gcloud run services update-traffic bilko-api \
  --to-revisions=bilko-api-00036=100 \
  --region=europe-north1

Rollback via GitHub Actions

git revert HEAD
git push origin main  # Triggers deploy workflow

Troubleshooting

Issue: CORS errors in browser

Cause: Custom domain not in bilko-cors-origins secret
Fix: Update secret (see step 4 in Custom Domain Runbook), deploy new revision

Issue: 502 Bad Gateway

Cause: Service unhealthy or startup timeout
Fix: Check Cloud Run logs: gcloud run services logs read bilko-api --region=europe-north1 --limit=50

Issue: Database connection timeout

Cause: Cloud SQL Proxy misconfiguration or secret outdated
Fix: Verify bilko-database-url secret, check Cloud SQL instance status

Issue: Custom domain SSL pending

Cause: DNS not propagated or domain not verified in Search Console
Fix: Wait 15-30 min after DNS change, verify domain ownership in Search Console

Architecture Diagram

┌─────────────────┐
│  one.com DNS    │
│  bilko-demo.    │
│  alai.no        │
└────────┬────────┘
         │ CNAME
         ▼
┌─────────────────────────────┐
│  ghs.googlehosted.com       │
│  (Google Cloud Load Balancer)│
└────────┬────────────────────┘
         │
         ▼
┌─────────────────────────────┐       ┌──────────────────┐
│  bilko-web (Cloud Run)      │──────▶│  bilko-api       │
│  Next.js 15 + React 19      │  HTTP │  Express + TS    │
│  europe-north1              │       │  europe-north1   │
└─────────────────────────────┘       └────────┬─────────┘
                                               │
                                               ▼
                                      ┌─────────────────┐
                                      │  Cloud SQL      │
                                      │  PostgreSQL 16  │
                                      │  europe-north1  │
                                      └─────────────────┘