Preview Environments for Nuxt.js: Automated Per-PR Deployments with Bunnyshell
Why Preview Environments for Nuxt.js?
Every Nuxt.js team hits the same wall: you build a feature with server routes and SSR pages, test it locally with nuxi dev, push to staging — and it breaks because staging still has someone else's middleware changes. Or your SSR pages render stale data because two feature branches share the same database. Or the reviewer asks you to "deploy it somewhere" so they can test the OAuth flow, and you waste time configuring a one-off deployment that doesn't match production.
Preview environments solve this. Every pull request gets its own isolated deployment — Nuxt.js app with Nitro server, PostgreSQL database, Redis for caching — running in Kubernetes with production-like configuration. Reviewers click a link and see the actual running app, not just the diff.
With Bunnyshell, you get:
- Automatic deployment — A new environment spins up for every PR
- Production parity — Same Docker images, same database engine, same SSR behavior
- Isolation — Each PR environment is fully independent, no shared staging conflicts
- Automatic cleanup — Environments are destroyed when the PR is merged or closed
Choose Your Approach
Bunnyshell supports three ways to set up preview environments for Nuxt.js. Pick the one that fits your workflow:
| Approach | Best for | Complexity | CI/CD maintenance |
|---|---|---|---|
| Approach A: Bunnyshell UI | Teams that want the fastest setup with zero pipeline maintenance | Easiest | None — Bunnyshell manages webhooks automatically |
| Approach B: Docker Compose Import | Teams already using docker-compose.yml for local development | Easy | None — import converts to Bunnyshell config automatically |
| Approach C: Helm Charts | Teams with existing Helm infrastructure or complex K8s needs | Advanced | Optional — can use CLI or Bunnyshell UI |
All three approaches end the same way: a toggle in Bunnyshell Settings that enables automatic preview environments for every PR. No GitHub Actions, no GitLab CI pipelines to maintain — Bunnyshell adds webhooks to your Git provider and listens for PR events.
Prerequisites: Prepare Your Nuxt.js App
Regardless of which approach you choose, your Nuxt.js app needs a proper Docker setup and the right configuration for running in Kubernetes.
1. Understand the Nitro Output
Nuxt 3 uses Nitro as its server engine. When you run nuxt build, Nitro produces a standalone output in .output/ that includes everything needed to run the server:
1.output/
2├── server/
3│ ├── index.mjs ← The Nitro server entry point
4│ ├── chunks/
5│ └── node_modules/ ← Only production dependencies
6├── public/
7│ ├── _nuxt/ ← Client-side bundles
8│ └── ...
9└── nitro.jsonThis is already a self-contained server — no need for Nginx or a separate static file server. Nitro handles both SSR and static asset serving.
2. Configure Nuxt for Docker
In your nuxt.config.ts, ensure these settings:
1export default defineNuxtConfig({
2 devtools: { enabled: false },
3
4 // Nitro server configuration
5 nitro: {
6 // Optional: configure server routes, plugins, etc.
7 },
8
9 // Runtime config — accessible via useRuntimeConfig()
10 runtimeConfig: {
11 // Server-only keys (NUXT_*)
12 databaseUrl: '',
13 redisUrl: '',
14 authSecret: '',
15
16 // Public keys (NUXT_PUBLIC_*)
17 public: {
18 apiBase: '/api',
19 appName: 'MyApp',
20 },
21 },
22})Nuxt's runtime config is more Docker-friendly than Next.js. Both NUXT_* (server) and NUXT_PUBLIC_* (client) variables are resolved at runtime, not build time. This means you can change them without rebuilding the Docker image.
3. Create a Production-Ready Dockerfile
Nuxt.js with Nitro runs as a single Node.js process — no Nginx sidecar needed:
1# ── Stage 1: Install dependencies ──
2FROM node:20-alpine AS deps
3RUN apk add --no-cache libc6-compat
4WORKDIR /app
5
6COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
7
8RUN \
9 if [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
10 elif [ -f package-lock.json ]; then npm ci; \
11 elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm install --frozen-lockfile; \
12 else echo "No lockfile found." && exit 1; \
13 fi
14
15# ── Stage 2: Build the application ──
16FROM node:20-alpine AS builder
17WORKDIR /app
18
19COPY /app/node_modules ./node_modules
20COPY . .
21
22# Generate Prisma client if schema exists
23RUN if [ -f prisma/schema.prisma ]; then npx prisma generate; fi
24
25RUN npm run build
26
27# ── Stage 3: Production image ──
28FROM node:20-alpine AS runner
29WORKDIR /app
30
31ENV NODE_ENV=production
32
33RUN addgroup --system --gid 1001 nuxtjs
34RUN adduser --system --uid 1001 nuxtjs
35
36# Copy the Nitro standalone output
37COPY /app/.output ./.output
38
39# Copy Prisma files if they exist
40COPY /app/prisma ./prisma 2>/dev/null || true
41COPY /app/node_modules/.prisma ./node_modules/.prisma 2>/dev/null || true
42COPY /app/node_modules/@prisma ./node_modules/@prisma 2>/dev/null || true
43
44USER nuxtjs
45
46EXPOSE 3000
47
48# Nitro environment variables for container networking
49ENV HOST=0.0.0.0
50ENV PORT=3000
51ENV NITRO_HOST=0.0.0.0
52ENV NITRO_PORT=3000
53
54CMD ["node", ".output/server/index.mjs"]Both HOST=0.0.0.0 and NITRO_HOST=0.0.0.0 should be set. Nitro checks NITRO_HOST first, then falls back to HOST. Without either, the server only listens on 127.0.0.1, making it unreachable from outside the container.
4. Create a Health Check Endpoint
Create server/api/health.get.ts:
1export default defineEventHandler(() => {
2 return { status: 'ok', timestamp: Date.now() }
3})Nuxt server routes in the server/api/ directory are automatically available at /api/health. No additional configuration needed.
5. Environment Variables
Nuxt 3 has a clean runtime config model:
NUXT_PUBLIC_*— Available on both client and server, resolved at runtimeNUXT_*— Server-only, resolved at runtime
Update your .env.example:
1# Server-only (NUXT_ prefix maps to runtimeConfig keys)
2NUXT_DATABASE_URL=postgresql://postgres:password@postgres:5432/nuxtapp
3NUXT_REDIS_URL=redis://redis:6379
4NUXT_AUTH_SECRET=
5
6# Public (available on client and server)
7NUXT_PUBLIC_API_BASE=/api
8NUXT_PUBLIC_APP_NAME=MyAppUnlike Next.js, Nuxt's NUXT_PUBLIC_* variables are resolved at runtime, not build time. This means the same Docker image works across all preview environments — just change the environment variables. No rebuild needed.
Nuxt.js Deployment Checklist
- Multi-stage Dockerfile with Node 20 Alpine
-
HOST=0.0.0.0andNITRO_HOST=0.0.0.0set for container networking - Health check endpoint at
/api/health - Runtime config uses
NUXT_*andNUXT_PUBLIC_*prefixes -
NUXT_PUBLIC_*variables are runtime (no rebuild needed for URL changes) - Prisma client generation included in build stage
- Nitro
.output/directory copied as standalone - Non-root user (
nuxtjs) for security - Server routes (
/api/*) work out of the box with Nitro
Approach A: Bunnyshell UI — Zero CI/CD Maintenance
This is the easiest approach. You connect your repo, paste a YAML config, deploy, and flip a toggle. No CI/CD pipelines to write or maintain — Bunnyshell automatically adds webhooks to your Git provider and creates/destroys preview environments when PRs are opened/closed.
Step 1: Create a Project and Environment
- Log into Bunnyshell
- Click Create project and name it (e.g., "Nuxt.js App")
- Inside the project, click Create environment and name it (e.g., "nuxtjs-main")
Step 2: Define the Environment Configuration
Click Configuration in your environment view and paste this bunnyshell.yaml:
1kind: Environment
2name: nuxtjs-preview
3type: primary
4
5environmentVariables:
6 NUXT_AUTH_SECRET: SECRET["your-auth-secret"]
7 DB_PASSWORD: SECRET["your-db-password"]
8
9components:
10 # ── Nuxt.js Application ──
11 - kind: Application
12 name: nuxtjs-app
13 gitRepo: 'https://github.com/your-org/your-nuxtjs-repo.git'
14 gitBranch: main
15 gitApplicationPath: /
16 dockerCompose:
17 build:
18 context: .
19 dockerfile: Dockerfile
20 environment:
21 NODE_ENV: production
22 HOST: '0.0.0.0'
23 PORT: '3000'
24 NITRO_HOST: '0.0.0.0'
25 NITRO_PORT: '3000'
26 NUXT_DATABASE_URL: 'postgresql://nuxtapp:{{ env.vars.DB_PASSWORD }}@postgres:5432/nuxtapp'
27 NUXT_REDIS_URL: 'redis://redis:6379'
28 NUXT_AUTH_SECRET: '{{ env.vars.NUXT_AUTH_SECRET }}'
29 NUXT_PUBLIC_API_BASE: '/api'
30 NUXT_PUBLIC_APP_URL: 'https://{{ components.nuxtjs-app.ingress.hosts[0] }}'
31 ports:
32 - '3000:3000'
33 dependsOn:
34 - postgres
35 - redis
36 hosts:
37 - hostname: 'app-{{ env.base_domain }}'
38 path: /
39 servicePort: 3000
40
41 # ── PostgreSQL Database ──
42 - kind: Database
43 name: postgres
44 dockerCompose:
45 image: 'postgres:16-alpine'
46 environment:
47 POSTGRES_DB: nuxtapp
48 POSTGRES_USER: nuxtapp
49 POSTGRES_PASSWORD: '{{ env.vars.DB_PASSWORD }}'
50 ports:
51 - '5432:5432'
52
53 # ── Redis (Caching / Sessions) ──
54 - kind: Service
55 name: redis
56 dockerCompose:
57 image: 'redis:7-alpine'
58 ports:
59 - '6379:6379'
60
61volumes:
62 - name: postgres-data
63 mount:
64 component: postgres
65 containerPath: /var/lib/postgresql/data
66 size: 1GiKey architecture notes:
- No Nginx sidecar needed — Nitro handles both SSR and static asset serving on port 3000
- Runtime env vars —
NUXT_PUBLIC_*variables are resolved at runtime, so the same image works for all preview environments without rebuilding NITRO_HOST: '0.0.0.0'— Required for Nitro to accept connections from outside the container- Server routes included — All files in
server/api/are automatically served by Nitro
Replace your-org/your-nuxtjs-repo with your actual repository. Save the configuration.
Step 3: Deploy
Click the Deploy button, select your Kubernetes cluster, and click Deploy Environment. Bunnyshell will:
- Build your Nuxt.js Docker image (multi-stage, Nitro standalone output)
- Pull PostgreSQL and Redis images
- Deploy everything into an isolated Kubernetes namespace
- Generate HTTPS URLs automatically with DNS
Monitor the deployment in the environment detail page. When status shows Running, click Endpoints to access your live Nuxt.js app.
Step 4: Run Post-Deploy Commands
After deployment, run database setup commands:
1export BUNNYSHELL_TOKEN=your-api-token
2bns components list --environment ENV_ID --output json | jq '._embedded.item[] | {id, name}'
3
4# Run Prisma migrations
5bns exec COMPONENT_ID -c nuxtjs-app -- npx prisma migrate deploy
6
7# Or if using Drizzle
8bns exec COMPONENT_ID -c nuxtjs-app -- npx drizzle-kit push
9
10# Seed the database (if you have a seed script)
11bns exec COMPONENT_ID -c nuxtjs-app -- npx prisma db seedStep 5: Enable Automatic Preview Environments
This is the magic step — no CI/CD configuration needed:
- In your environment, go to Settings
- Find the Ephemeral environments section
- Toggle "Create ephemeral environments on pull request" to ON
- Toggle "Destroy environment after merge or close pull request" to ON
- Select the Kubernetes cluster for ephemeral environments
That's it. Bunnyshell automatically adds a webhook to your Git provider (GitHub, GitLab, or Bitbucket). From now on:
- Open a PR — Bunnyshell creates an ephemeral environment with the PR's branch
- Push to PR — The environment redeploys with the latest changes
- Bunnyshell posts a comment on the PR with a link to the live deployment
- Merge or close the PR — The ephemeral environment is automatically destroyed
The primary environment must be in Running or Stopped status before ephemeral environments can be created from it.
Approach B: Docker Compose Import
Already have a docker-compose.yml for local development? Bunnyshell can import it directly and convert it to its environment format. No manual YAML writing required.
Step 1: Add a docker-compose.yml to Your Repo
If you don't already have one, create docker-compose.yml in your repo root:
1version: '3.8'
2
3services:
4 nuxtjs-app:
5 build:
6 context: .
7 dockerfile: Dockerfile
8 ports:
9 - '3000:3000'
10 environment:
11 NODE_ENV: production
12 HOST: '0.0.0.0'
13 PORT: '3000'
14 NITRO_HOST: '0.0.0.0'
15 NITRO_PORT: '3000'
16 NUXT_DATABASE_URL: 'postgresql://nuxtapp:secret@postgres:5432/nuxtapp'
17 NUXT_REDIS_URL: 'redis://redis:6379'
18 NUXT_AUTH_SECRET: 'dev-secret-change-in-production'
19 NUXT_PUBLIC_API_BASE: '/api'
20 NUXT_PUBLIC_APP_URL: 'http://localhost:3000'
21 depends_on:
22 - postgres
23 - redis
24
25 postgres:
26 image: postgres:16-alpine
27 environment:
28 POSTGRES_DB: nuxtapp
29 POSTGRES_USER: nuxtapp
30 POSTGRES_PASSWORD: secret
31 volumes:
32 - postgres-data:/var/lib/postgresql/data
33 ports:
34 - '5432:5432'
35
36 redis:
37 image: redis:7-alpine
38 ports:
39 - '6379:6379'
40
41volumes:
42 postgres-data:Step 2: Import into Bunnyshell
- Create a Project and Environment in Bunnyshell (same as Approach A, Step 1)
- Click Define environment
- Select your Git account and repository
- Set the branch (e.g.,
main) and the path todocker-compose.yml(use/if it's in the root) - Click Continue — Bunnyshell parses and validates your Docker Compose file
Bunnyshell automatically detects:
- All services (nuxtjs-app, postgres, redis)
- Exposed ports
- Build configurations (Dockerfiles)
- Volumes
- Environment variables
It converts everything into a bunnyshell.yaml environment definition.
The docker-compose.yml is only read during the initial import. Subsequent changes to the file won't auto-propagate — edit the environment configuration in Bunnyshell instead.
Step 3: Adjust the Configuration
After import, go to Configuration in the environment view and update:
Replace hardcoded secrets with SECRET["..."] syntax:
1environmentVariables:
2 NUXT_AUTH_SECRET: SECRET["your-auth-secret"]
3 DB_PASSWORD: SECRET["your-db-password"]Add dynamic URLs using Bunnyshell interpolation:
NUXT_PUBLIC_APP_URL: 'https://{{ components.nuxtjs-app.ingress.hosts[0] }}'
NUXT_DATABASE_URL: 'postgresql://nuxtapp:{{ env.vars.DB_PASSWORD }}@postgres:5432/nuxtapp'Step 4: Deploy and Enable Preview Environments
Same as Approach A — click Deploy, then go to Settings and toggle on ephemeral environments.
Best Practices for Docker Compose with Bunnyshell
- No Nginx sidecar needed — Nitro handles both SSR and static asset serving. Keep it as a single container
- Remove local volumes —
volumes: ['.:/app']is for local dev (hot module replacement). Remove these in Bunnyshell — the Docker image already contains the built app - Use Bunnyshell interpolation for dynamic values:
1# Local docker-compose.yml
2NUXT_PUBLIC_APP_URL: http://localhost:3000
3
4# Bunnyshell environment config (after import)
5NUXT_PUBLIC_APP_URL: 'https://{{ components.nuxtjs-app.ingress.hosts[0] }}'- Runtime config advantage — Unlike Next.js, Nuxt's
NUXT_PUBLIC_*variables are resolved at runtime. The same Docker image works across all environments — just update the environment variables in Bunnyshell
Approach C: Helm Charts
For teams with existing Helm infrastructure or complex Kubernetes requirements (custom ingress, service mesh, advanced scaling). Helm gives you full control over every Kubernetes resource.
Step 1: Create a Helm Chart
Structure your Nuxt.js Helm chart in your repo:
1helm/nuxtjs/
2├── Chart.yaml
3├── values.yaml
4└── templates/
5 ├── deployment.yaml
6 ├── service.yaml
7 ├── ingress.yaml
8 ├── configmap.yaml
9 └── migration-job.yamlA minimal values.yaml:
1replicaCount: 1
2image:
3 repository: ""
4 tag: latest
5service:
6 port: 3000
7ingress:
8 enabled: true
9 className: bns-nginx
10 host: ""
11env:
12 NODE_ENV: production
13 HOST: "0.0.0.0"
14 PORT: "3000"
15 NITRO_HOST: "0.0.0.0"
16 NITRO_PORT: "3000"
17 NUXT_DATABASE_URL: ""
18 NUXT_REDIS_URL: ""
19 NUXT_AUTH_SECRET: ""
20 NUXT_PUBLIC_API_BASE: "/api"
21 NUXT_PUBLIC_APP_URL: ""Step 2: Define the Bunnyshell Configuration
Create a bunnyshell.yaml using Helm components:
1kind: Environment
2name: nuxtjs-helm
3type: primary
4
5environmentVariables:
6 NUXT_AUTH_SECRET: SECRET["your-auth-secret"]
7 DB_PASSWORD: SECRET["your-db-password"]
8 PG_DATABASE: nuxtapp
9 PG_USER: nuxtapp
10
11components:
12 # ── Docker Image Build ──
13 - kind: DockerImage
14 name: nuxtjs-image
15 context: /
16 dockerfile: Dockerfile
17 gitRepo: 'https://github.com/your-org/your-nuxtjs-repo.git'
18 gitBranch: main
19 gitApplicationPath: /
20
21 # ── PostgreSQL via Helm (Bitnami) ──
22 - kind: Helm
23 name: postgres
24 runnerImage: 'dtzar/helm-kubectl:3.8.2'
25 deploy:
26 - |
27 cat << EOF > pg_values.yaml
28 global:
29 storageClass: bns-network-sc
30 auth:
31 postgresPassword: {{ env.vars.DB_PASSWORD }}
32 database: {{ env.vars.PG_DATABASE }}
33 username: {{ env.vars.PG_USER }}
34 password: {{ env.vars.DB_PASSWORD }}
35 EOF
36 - 'helm repo add bitnami https://charts.bitnami.com/bitnami'
37 - 'helm upgrade --install --namespace {{ env.k8s.namespace }}
38 --post-renderer /bns/helpers/helm/bns_post_renderer
39 -f pg_values.yaml postgres bitnami/postgresql --version 13.4.4'
40 - |
41 PG_HOST="postgres-postgresql.{{ env.k8s.namespace }}.svc.cluster.local"
42 destroy:
43 - 'helm uninstall postgres --namespace {{ env.k8s.namespace }}'
44 start:
45 - 'kubectl scale --replicas=1 --namespace {{ env.k8s.namespace }}
46 statefulset/postgres-postgresql'
47 stop:
48 - 'kubectl scale --replicas=0 --namespace {{ env.k8s.namespace }}
49 statefulset/postgres-postgresql'
50 exportVariables:
51 - PG_HOST
52
53 # ── Nuxt.js App via Helm ──
54 - kind: Helm
55 name: nuxtjs-app
56 runnerImage: 'dtzar/helm-kubectl:3.8.2'
57 deploy:
58 - |
59 cat << EOF > nuxtjs_values.yaml
60 replicaCount: 1
61 image:
62 repository: {{ components.nuxtjs-image.image }}
63 service:
64 port: 3000
65 ingress:
66 enabled: true
67 className: bns-nginx
68 host: app-{{ env.base_domain }}
69 env:
70 NODE_ENV: production
71 HOST: '0.0.0.0'
72 PORT: '3000'
73 NITRO_HOST: '0.0.0.0'
74 NITRO_PORT: '3000'
75 NUXT_DATABASE_URL: 'postgresql://{{ env.vars.PG_USER }}:{{ env.vars.DB_PASSWORD }}@{{ components.postgres.exported.PG_HOST }}:5432/{{ env.vars.PG_DATABASE }}'
76 NUXT_REDIS_URL: 'redis://redis:6379'
77 NUXT_AUTH_SECRET: '{{ env.vars.NUXT_AUTH_SECRET }}'
78 NUXT_PUBLIC_API_BASE: '/api'
79 NUXT_PUBLIC_APP_URL: 'https://app-{{ env.base_domain }}'
80 EOF
81 - 'helm upgrade --install --namespace {{ env.k8s.namespace }}
82 --post-renderer /bns/helpers/helm/bns_post_renderer
83 -f nuxtjs_values.yaml nuxtjs-{{ env.unique }} ./helm/nuxtjs'
84 destroy:
85 - 'helm uninstall nuxtjs-{{ env.unique }} --namespace {{ env.k8s.namespace }}'
86 start:
87 - 'helm upgrade --namespace {{ env.k8s.namespace }}
88 --post-renderer /bns/helpers/helm/bns_post_renderer
89 --reuse-values --set replicaCount=1 nuxtjs-{{ env.unique }} ./helm/nuxtjs'
90 stop:
91 - 'helm upgrade --namespace {{ env.k8s.namespace }}
92 --post-renderer /bns/helpers/helm/bns_post_renderer
93 --reuse-values --set replicaCount=0 nuxtjs-{{ env.unique }} ./helm/nuxtjs'
94 gitRepo: 'https://github.com/your-org/your-nuxtjs-repo.git'
95 gitBranch: main
96 gitApplicationPath: /helm/nuxtjs
97
98 # ── Redis ──
99 - kind: Service
100 name: redis
101 dockerCompose:
102 image: 'redis:7-alpine'
103 ports:
104 - '6379:6379'Always include --post-renderer /bns/helpers/helm/bns_post_renderer in your helm commands. This adds labels so Bunnyshell can track resources, show logs, and manage component lifecycle.
Step 3: Deploy and Enable Preview Environments
Same flow: paste the config in Configuration, hit Deploy, then enable ephemeral environments in Settings.
Enabling Preview Environments (All Approaches)
Regardless of which approach you used, enabling automatic preview environments is the same:
- Ensure your primary environment has been deployed at least once (Running or Stopped status)
- Go to Settings in your environment
- Toggle "Create ephemeral environments on pull request" to ON
- Toggle "Destroy environment after merge or close pull request" to ON
- Select the target Kubernetes cluster
What happens next:
- Bunnyshell adds a webhook to your Git provider automatically
- When a developer opens a PR, Bunnyshell creates an ephemeral environment cloned from the primary, using the PR's branch
- Bunnyshell posts a comment on the PR with a direct link to the running deployment
- When the PR is merged or closed, the ephemeral environment is automatically destroyed
No GitHub Actions. No GitLab CI pipelines. No maintenance. It just works.
Optional: CI/CD Integration via CLI
If you prefer to control preview environments from your CI/CD pipeline (e.g., for custom migration or seed scripts), you can use the Bunnyshell CLI:
1# Install
2brew install bunnyshell/tap/bunnyshell-cli
3
4# Authenticate
5export BUNNYSHELL_TOKEN=your-api-token
6
7# Create, deploy, and run migrations in one flow
8bns environments create --from-path bunnyshell.yaml --name "pr-123" --project PROJECT_ID --k8s CLUSTER_ID
9bns environments deploy --id ENV_ID --wait
10bns exec COMPONENT_ID -c nuxtjs-app -- npx prisma migrate deployRemote Development and Debugging
Bunnyshell makes it easy to develop and debug directly against any environment — primary or ephemeral:
Port Forwarding
Connect your local tools to the remote database:
1# Forward PostgreSQL to local port 15432
2bns port-forward 15432:5432 --component PG_COMPONENT_ID
3
4# Connect with psql, pgAdmin, or any DB tool
5psql -h 127.0.0.1 -p 15432 -U nuxtapp -d nuxtapp
6
7# Forward Redis to local port 16379
8bns port-forward 16379:6379 --component REDIS_COMPONENT_ID
9redis-cli -p 16379Execute Commands
1# Run Prisma migrations
2bns exec COMPONENT_ID -c nuxtjs-app -- npx prisma migrate deploy
3
4# Or Drizzle migrations
5bns exec COMPONENT_ID -c nuxtjs-app -- npx drizzle-kit push
6
7# Check migration status
8bns exec COMPONENT_ID -c nuxtjs-app -- npx prisma migrate status
9
10# Seed the database
11bns exec COMPONENT_ID -c nuxtjs-app -- npx prisma db seed
12
13# Check Nitro server info
14bns exec COMPONENT_ID -c nuxtjs-app -- node -e "console.log(require('./.output/nitro.json'))"
15
16# List available server routes
17bns exec COMPONENT_ID -c nuxtjs-app -- ls -la .output/server/chunks/routes/Live Logs
1# Stream logs in real time
2bns logs --component COMPONENT_ID -f
3
4# Last 200 lines
5bns logs --component COMPONENT_ID --tail 200
6
7# Logs from the last 5 minutes
8bns logs --component COMPONENT_ID --since 5mLive Code Sync
For active development, sync your local code changes to the remote container in real time:
1bns remote-development up --component COMPONENT_ID
2# Edit files locally — changes sync automatically
3# When done:
4bns remote-development downSince Nuxt's NUXT_PUBLIC_* variables are runtime, you can update them without rebuilding. For code changes to server routes and Vue components, live code sync triggers Nitro to reload automatically.
Troubleshooting
| Issue | Solution |
|---|---|
| 502 Bad Gateway | Nitro server not listening. Verify NITRO_HOST=0.0.0.0 and HOST=0.0.0.0 are set — without them, Nitro only binds to 127.0.0.1. Check port 3000 is exposed and matches servicePort in hosts config. |
| Health check fails | Missing /api/health server route. Create server/api/health.get.ts returning a JSON response. |
| SSR pages show wrong URL | Check NUXT_PUBLIC_APP_URL is set correctly using Bunnyshell interpolation: 'https://{{ components.nuxtjs-app.ingress.hosts[0] }}' |
| Prisma: "Can't reach database server" | Check NUXT_DATABASE_URL uses postgres (the component name) as host, not localhost. Verify PostgreSQL is running before running migrations. |
| Drizzle migration fails | Ensure drizzle-kit is in dependencies (not just devDependencies) or install it in the Dockerfile build stage. |
| Static assets return 404 | Nitro serves static assets from .output/public/. Verify the build completed successfully and .output/public/_nuxt/ contains the client bundles. |
| Server routes return 500 | Check Nitro logs for the actual error. Common cause: missing environment variables for database connections. Use useRuntimeConfig() and ensure all NUXT_* variables are set. |
| Hybrid rendering not working | Route rules defined in nuxt.config.ts are baked at build time. Verify routeRules are correct and the build log shows the expected pre-rendered routes. |
| WebSocket connections fail | Nitro supports WebSockets via defineWebSocketHandler. Ensure the Kubernetes ingress supports WebSocket upgrades — add annotations: nginx.ingress.kubernetes.io/proxy-read-timeout: "3600". |
| CORS errors on API routes | Add CORS headers in a Nitro server middleware: create server/middleware/cors.ts using setResponseHeaders. |
What's Next?
- Add database migrations to deploy hooks — Automate
npx prisma migrate deployornpx drizzle-kit pushas a post-deploy step - Configure hybrid rendering — Use
routeRulesto pre-render marketing pages while keeping dynamic routes SSR - Add S3/MinIO — For file uploads and image storage (
minio/minioas a Service component) - Add Storybook — Deploy Storybook alongside your Nuxt.js app for component review with
@storybook/vue3 - Monitor with Nitro plugins — Add request tracing and performance monitoring via Nitro server plugins
Related Resources
- Bunnyshell Quickstart Guide
- Docker Compose with Bunnyshell
- Helm with Bunnyshell
- Bunnyshell CLI Reference
- Preview Environments for Next.js — Same pattern for Next.js/React
- Ephemeral Environments — Learn more about the concept
- All Guides — More technical guides
Ship faster starting today.
14-day full-feature trial. No credit card required. Pay-as-you-go from $0.007/min per environment.