Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.markup.freddiephilpot.dev/llms.txt

Use this file to discover all available pages before exploring further.

Deployment

Markup can be deployed as a standard Next.js application using platforms such as:
  • Vercel
  • Docker
  • VPS / Dedicated Servers
  • Railway
  • Coolify
  • Render
The application requires:
  • PostgreSQL database
  • Environment variables configured
  • Prisma migrations applied

Production Requirements

Before deployment, ensure you have:
  • Node.js 18+
  • PostgreSQL database
  • WorkOS application configured
  • Production environment variables configured
  • HTTPS enabled for authentication callbacks

Required Environment Variables

Example production configuration:
DATABASE_URL=postgresql://user:password@host:5432/markup?sslmode=require

WORKOS_CLIENT_ID=client_xxxxx
WORKOS_API_KEY=sk_live_xxxxx
WORKOS_COOKIE_PASSWORD=secure_random_string

NEXT_PUBLIC_WORKOS_REDIRECT_URI=https://yourdomain.com/api/auth/callback

NEXT_PUBLIC_DB_PROVIDER=postgres/convex

Build the Application

Install dependencies:
npm install
Generate the Prisma client:
npx prisma generate
Build the production application:
npm run build
Start the production server:
npm run start

Database Migrations

Run Prisma migrations before starting production deployments:
npx prisma migrate deploy
This applies all pending migrations safely in production.

Deploying to Vercel

1. Push Repository to GitHub

Ensure your repository is committed and pushed.

2. Import Project into Vercel

  1. Open the Vercel dashboard
  2. Import the GitHub repository
  3. Select the project
  4. Configure environment variables

3. Configure Environment Variables

Add all required variables from .env.local into:
Vercel Project Settings → Environment Variables

4. Configure PostgreSQL

You can use:
  • Neon
  • Supabase
  • Railway PostgreSQL
  • Vercel Postgres
  • Self-hosted PostgreSQL
Ensure SSL is enabled if required by the provider.

5. Deploy

Vercel automatically runs:
npm run build
After deployment, run migrations:
npx prisma migrate deploy

Deploying with Docker

Docker Compose

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: markup
      POSTGRES_PASSWORD: markup_password
      POSTGRES_DB: markup
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U markup -d markup"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NEXT_PUBLIC_DB_PROVIDER: postgres
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "3001:3000"
    environment:
      # Database — points to the postgres service above
      DATABASE_URL: postgresql://markup:markup_password@db:5432/markup

      # WorkOS auth — fill these in or use a .env file
      WORKOS_CLIENT_ID: ${WORKOS_CLIENT_ID}
      WORKOS_API_KEY: ${WORKOS_API_KEY}
      WORKOS_COOKIE_PASSWORD: ${WORKOS_COOKIE_PASSWORD}
      NEXT_PUBLIC_WORKOS_REDIRECT_URI: ${NEXT_PUBLIC_WORKOS_REDIRECT_URI}

      # App URL (used for CSP / redirect base)
      NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-http://localhost:3000}

      # Use Postgres instead of Convex for self-hosted deployments
      NEXT_PUBLIC_DB_PROVIDER: postgres

      NODE_ENV: production

volumes:
  postgres_data:

Dockerfile

FROM node:20-alpine AS base

# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat openssl
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts --legacy-peer-deps

# Rebuild the source code only when needed
FROM base AS builder
RUN apk add --no-cache openssl
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Accept public env vars as build args so Next.js can bake them into the bundle
ARG NEXT_PUBLIC_DB_PROVIDER=postgres
ENV NEXT_PUBLIC_DB_PROVIDER=$NEXT_PUBLIC_DB_PROVIDER

# Generate Prisma client (dummy DATABASE_URL satisfies schema validation at build time)
RUN DATABASE_URL="postgresql://dummy:dummy@localhost:5432/dummy" node node_modules/prisma/build/index.js generate

# Build the Next.js app (no Convex deploy, just the Next build)
RUN npm run build:docker

# Production image
FROM base AS runner
RUN apk add --no-cache openssl
WORKDIR /app

ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Leverage Next.js standalone output
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

# Copy Prisma schema and migrations so we can run migrations at startup
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
COPY --from=builder /app/node_modules/prisma ./node_modules/prisma

COPY docker-entrypoint.sh ./docker-entrypoint.sh
RUN chmod +x ./docker-entrypoint.sh

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

ENTRYPOINT ["./docker-entrypoint.sh"]

Deploying on a VPS

Clone the Repository

git clone https://github.com/pphilfre/markup.git
cd markup

Install Dependencies

npm install

Configure Environment Variables

Create:
.env.production
Add all required production variables.

Generate Prisma Client

npx prisma generate

Run Migrations

npx prisma migrate deploy

Build Application

npm run build

Start Server

npm run start

Reverse Proxy Example (Nginx)

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;

        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';

        proxy_set_header Host $host;

        proxy_cache_bypass $http_upgrade;
    }
}

Recommended Production Setup

Recommended stack:
  • Next.js application
  • PostgreSQL database
  • Nginx reverse proxy
  • HTTPS via Let’s Encrypt
  • PM2 or Docker for process management

PM2 Example

Install PM2:
npm install -g pm2
Start the application:
pm2 start npm --name markup -- run start
Save PM2 configuration:
pm2 save
Enable startup:
pm2 startup

Updating Production Deployments

Pull latest changes:
git pull
Install dependencies:
npm install
Run migrations:
npx prisma migrate deploy
Rebuild application:
npm run build
Restart application:
pm2 restart markup

Security Recommendations

  • Use HTTPS in production
  • Enable PostgreSQL SSL
  • Restrict database access
  • Never expose .env files publicly
  • Use strong secrets
  • Rotate API keys periodically
  • Enable automatic backups

Troubleshooting

Prisma Migration Failures

Check migration status:
npx prisma migrate status

Application Fails to Start

Verify:
  • Environment variables exist
  • Database is reachable
  • Prisma client is generated
  • Build completed successfully

WorkOS Authentication Errors

Ensure:
  • Redirect URI matches production domain
  • HTTPS is enabled
  • Callback URLs are configured in WorkOS

Deployment Checklist

  1. Environment variables configured
  2. PostgreSQL database running
  3. Prisma migrations applied
  4. HTTPS enabled
  5. WorkOS redirect URI configured
  6. Application successfully built
  7. Backups configured
  8. Monitoring/logging enabled