Skip to main content

Deployment Guide

Deploy your React/Vite static site to Dokku and connect it to the Users API.

Overview

This guide covers:

  1. Preparing your project for Dokku deployment
  2. Setting up Dokku app
  3. Building and deploying your static site
  4. Configuring nginx for SPA routing

Prerequisites

  • Dokku instance with SSH access
  • Git repository for your project
  • Users API configuration already set in src/config/users.ts (hardcoded production values)

Step 1: Prepare Your Project

Package.json Scripts

Ensure your package.json has these scripts:

{
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "npx serve -s dist -l $PORT",
"preview": "vite preview"
}
}

Key points:

  • build: Creates production build in dist/ directory
  • start: Serves the static site (uses $PORT for Dokku compatibility)
  • Uses npx serve for minimal runtime (no Node.js needed after build)

Create Procfile

Create a Procfile in your project root:

web: npm run start

This tells Dokku how to run your app.

For a smaller runtime image (nginx-only, no Node.js), create a multi-stage Dockerfile:

# ---- build stage ----
FROM node:20-alpine AS build
WORKDIR /app

# Enable pnpm (if using pnpm)
RUN corepack enable

# Copy manifests first for better caching
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Copy source and build
COPY . .
RUN pnpm build

# ---- runtime stage (no node) ----
FROM nginxinc/nginx-unprivileged:1.27-alpine AS runtime

EXPOSE 8080

# SPA routing configuration
COPY nginx/default.conf /etc/nginx/conf.d/default.conf

# Copy built static files
COPY --from=build /app/dist /usr/share/nginx/html

Note: If using npm instead of pnpm, adjust the Dockerfile accordingly.

Create Nginx Configuration

Create nginx/default.conf:

server {
listen 8080;
server_name _;

root /usr/share/nginx/html;
index index.html;

# Cache Vite hashed assets aggressively
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}

# SPA fallback: serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
}

Important: The port must be 8080 for Dokku compatibility.

Verify Configuration

Ensure your src/config/users.ts has production values hardcoded:

export const USERS_API_BASE_URL =
import.meta.env.VITE_USERS_API_BASE_URL || "https://users-api.brainylab.io";

export const USERS_APP_ID = "my-app-id";

Note: No environment variables needed in production - values are hardcoded in the config file.

Step 2: Configure Dokku App

Create Dokku App

SSH into your Dokku host and create the app:

dokku apps:create my-static-app

Configure Port (if using Procfile)

Dokku automatically sets PORT, but ensure your start script uses it:

{
"scripts": {
"start": "npx serve -s dist -l $PORT"
}
}

Step 3: Deploy

Add Dokku Remote

Add Dokku as a git remote:

git remote add dokku dokku@YOUR_DOKKU_HOST:my-static-app

Replace YOUR_DOKKU_HOST with your Dokku server's hostname or IP.

Deploy

Push to Dokku:

git push dokku main

Or if your default branch is master:

git push dokku master

What Happens During Deploy

  1. Dokku detects your Dockerfile (if present) or uses buildpacks
  2. Builds your app (npm install + npm run build)
  3. Runs your start script or serves via nginx (if using Dockerfile)
  4. App becomes available at http://my-static-app.your-domain.com

Step 4: Verify Deployment

Check App Status

dokku ps:report my-static-app

View Logs

dokku logs my-static-app

Test Your Site

Visit your app URL and verify:

  • Site loads correctly
  • Form submission works
  • API calls succeed (check browser console)

Step 5: Update Configuration

If you need to change the API URL or App ID:

  1. Update src/config/users.ts with new values
  2. Commit and push changes:
git add src/config/users.ts
git commit -m "Update API configuration"
git push dokku main

Note: Configuration is hardcoded in the source code, so changes require a new deployment.

Alternative: Using Buildpacks (Simpler)

If you prefer not to use Docker:

1. Remove Dockerfile

Dokku will use Node.js buildpacks automatically.

2. Ensure package.json has build script

{
"scripts": {
"build": "vite build",
"start": "npx serve -s dist -l $PORT"
}
}

3. Deploy

git push dokku main

Dokku will:

  • Detect Node.js project
  • Run npm install
  • Run npm run build (if heroku-postbuild script exists, or manually)
  • Run npm start via Procfile

4. Add Build Script (Optional)

To ensure build runs automatically, add to package.json:

{
"scripts": {
"heroku-postbuild": "npm run build"
}
}

Troubleshooting

Build Fails

Check logs:

dokku logs my-static-app -t

Common issues:

  • Build script errors
  • Dependency installation failures
  • Configuration errors in src/config/users.ts

App Won't Start

Check process status:

dokku ps:report my-static-app

Common issues:

  • Port conflicts
  • Missing start script
  • Procfile syntax errors

API Connection Issues

Verify configuration:

  • Check src/config/users.ts has correct production API URL
  • Ensure USERS_APP_ID matches your app identifier
  • For local testing, create .env file with VITE_USERS_API_BASE_URL override

SPA Routing Not Working

If using nginx Dockerfile, ensure nginx/default.conf has the SPA fallback:

location / {
try_files $uri $uri/ /index.html;
}

If using serve, it should handle SPA routing automatically with -s flag.

CORS Errors

Ensure your Users API has your Dokku domain in CORS_ORIGINS:

# On Users API instance
dokku config:set users-api \
CORS_ORIGINS=https://my-static-app.your-domain.com

Best Practices

1. Use Dockerfile for Smaller Images

The nginx-based Dockerfile produces smaller runtime images (~50MB vs ~200MB+ with Node.js).

2. Verify Configuration Before Deploy

Ensure src/config/users.ts has correct production values before deploying.

3. Use Git Tags for Releases

Tag releases for easier rollbacks:

git tag v1.0.0
git push dokku v1.0.0

4. Monitor Logs

Regularly check logs for errors:

dokku logs my-static-app -t

5. Test Locally First

Build and test locally before deploying:

npm run build
npm run start
# Visit http://localhost:3000

Example: Complete Project Structure

my-static-app/
├── .env.example
├── .gitignore
├── Dockerfile # Optional: for nginx runtime
├── Procfile # Required: for Dokku
├── nginx/
│ └── default.conf # Required if using Dockerfile
├── package.json
├── pnpm-lock.yaml # or package-lock.json
├── src/
│ ├── components/
│ ├── config/
│ │ └── users.ts
│ ├── lib/
│ │ └── api.ts
│ └── App.tsx
├── index.html
└── vite.config.ts

Next Steps