Skip to main content
This guide covers deploying the Shopify Subscriptions Reference App to production, including hosting recommendations, build process, and security hardening.

Pre-Deployment Checklist

Before deploying to production, ensure:
  • All prerequisites are met
  • Database is configured with a production-grade provider
  • Environment variables are set correctly
  • SSL certificate is configured
  • Domain is configured and pointed to hosting provider
  • Shopify app is created in Partner Dashboard
  • OAuth redirect URLs are configured
  • Webhook endpoints are accessible

Build Process

Production Build

The application uses Remix with Vite for building:
# Build the application
pnpm run build
This command:
  1. Sets NODE_ENV=production
  2. Runs remix vite:build
  3. Generates optimized production assets
  4. Creates server bundle in build/server/
  5. Creates client assets in build/client/
The build output includes source maps for debugging production issues.

Setup Database

Run database setup before starting the server:
# Generate Prisma Client and run migrations
pnpm run setup
This executes:
  • prisma generate - Generates TypeScript types and Prisma Client
  • prisma migrate deploy - Applies pending migrations
Always run migrations before starting your application server to ensure database schema is up to date.

Start Production Server

Start the production server:
pnpm start
# Runs: remix-serve ./build/server/index.js
The server will:
  • Listen on PORT environment variable (default: 3000)
  • Serve the built application
  • Handle Shopify OAuth and webhook requests

Hosting Recommendations

Vercel provides excellent support for Remix applications.
1

Install Vercel CLI

npm i -g vercel
2

Configure vercel.json

Create vercel.json in your project root:
{
  "buildCommand": "pnpm run build",
  "devCommand": "pnpm dev",
  "installCommand": "pnpm install",
  "framework": "remix",
  "outputDirectory": "build/client",
  "regions": ["iad1"],
  "env": {
    "NODE_ENV": "production"
  }
}
3

Deploy

# First deployment
vercel --prod

# Set environment variables
vercel env add SHOPIFY_API_KEY
vercel env add SHOPIFY_API_SECRET
vercel env add DATABASE_URL
# ... add all required variables
4

Configure Domain

  1. Go to Vercel Dashboard → Your Project → Settings → Domains
  2. Add your custom domain
  3. Update DNS records as instructed
  4. Update SHOPIFY_APP_URL environment variable

Railway

Railway offers simple deployment with integrated PostgreSQL.
1

Create Railway Project

  1. Go to railway.app
  2. Sign in with GitHub
  3. Click New Project
  4. Select Deploy from GitHub repo
2

Add PostgreSQL

  1. Click NewDatabasePostgreSQL
  2. Railway automatically creates database
  3. Copy DATABASE_URL from database settings
3

Configure Build

Set environment variables in Railway dashboard:
# Build configuration
NODE_ENV=production

# Shopify credentials
SHOPIFY_API_KEY=your_key
SHOPIFY_API_SECRET=your_secret
SCOPES=customer_read_customers,...
SHOPIFY_APP_URL=https://your-app.up.railway.app

# Database (automatically set by Railway)
DATABASE_URL=${{Postgres.DATABASE_URL}}
4

Configure Deploy

Railway auto-detects build commands from package.json:
  • Build Command: pnpm run build
  • Start Command: pnpm start
Customize in railway.toml if needed:
[build]
builder = "nixpacks"
buildCommand = "pnpm install && pnpm run setup && pnpm run build"

[deploy]
startCommand = "pnpm start"

Render

Render provides managed hosting with PostgreSQL.
1

Create Web Service

  1. Go to render.com
  2. Click NewWeb Service
  3. Connect your GitHub repository
  4. Configure:
    • Name: Your app name
    • Environment: Node
    • Build Command: pnpm install && pnpm run setup && pnpm run build
    • Start Command: pnpm start
2

Add PostgreSQL

  1. Click NewPostgreSQL
  2. Create database
  3. Copy Internal Database URL
  4. Add as DATABASE_URL environment variable
3

Configure Environment

Add environment variables in Render dashboard

Heroku

Heroku offers straightforward deployment with add-ons.
1

Install Heroku CLI

npm install -g heroku
heroku login
2

Create Heroku App

heroku create your-app-name
3

Add PostgreSQL

heroku addons:create heroku-postgresql:mini
# DATABASE_URL automatically set
4

Configure Environment

heroku config:set SHOPIFY_API_KEY="your_key"
heroku config:set SHOPIFY_API_SECRET="your_secret"
heroku config:set SCOPES="customer_read_customers,..."
heroku config:set NODE_ENV="production"
5

Create Procfile

# Procfile
web: pnpm start
release: pnpm run setup
6

Deploy

git push heroku main

AWS, GCP, Azure

For enterprise deployments:
  • AWS: Deploy on ECS, Elastic Beanstalk, or EC2
  • Google Cloud: Use Cloud Run, App Engine, or Compute Engine
  • Azure: Deploy to App Service or Container Instances
Cloud platform deployment requires more configuration but offers greater control and scalability.

Shopify App Configuration

Update App URLs

After deployment, update your Shopify app configuration:
1

Application URL

  1. Go to Partner Dashboard → Apps → Your App
  2. Update App URL: https://your-app.example.com
2

Allowed Redirection URLs

Add OAuth redirect URLs:
https://your-app.example.com/auth/callback
https://your-app.example.com/auth/shopify/callback
https://your-app.example.com/api/auth/callback
3

Update shopify.app.toml

Update shopify.app.toml with production URL:
application_url = "https://your-app.example.com"

[auth]
redirect_urls = [
  "https://your-app.example.com/auth/callback",
  "https://your-app.example.com/auth/shopify/callback",
  "https://your-app.example.com/api/auth/callback",
]

Configure Webhooks

Verify webhook endpoints are accessible:
# Test webhook endpoint
curl https://your-app.example.com/webhooks/app/uninstalled
Webhooks configured in shopify.app.toml:
  • /webhooks/app/uninstalled
  • /webhooks/subscription_contracts/create
  • /webhooks/subscription_contracts/activate
  • /webhooks/subscription_contracts/cancel
  • /webhooks/subscription_contracts/pause
  • /webhooks/subscription_billing_cycles/skip
  • /webhooks/subscription_billing_attempts/success
  • /webhooks/subscription_billing_attempts/failure
  • /webhooks/selling_plan_groups/create_or_update
  • /webhooks/customer_redact
  • /webhooks/shop_redact

Security Hardening

1

Enable HTTPS Only

Ensure all traffic uses HTTPS:
  • Configure SSL certificate on hosting provider
  • Enable automatic HTTPS redirects
  • Set Strict-Transport-Security header
Shopify requires embedded apps to use HTTPS. HTTP connections will fail.
2

Secure Environment Variables

  • Never commit secrets to version control
  • Use hosting provider’s secret management
  • Rotate secrets regularly (every 90 days)
  • Audit access to production secrets
3

Database Security

  • Enable SSL connections: ?sslmode=require
  • Use strong passwords (20+ characters)
  • Restrict database access to application IPs only
  • Enable automated backups
  • Implement connection pooling
4

Set Security Headers

Add security headers to your application:
// app/entry.server.tsx
export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  // Security headers
  responseHeaders.set('X-Frame-Options', 'DENY');
  responseHeaders.set('X-Content-Type-Options', 'nosniff');
  responseHeaders.set('Referrer-Policy', 'strict-origin-when-cross-origin');
  responseHeaders.set('X-XSS-Protection', '1; mode=block');
  responseHeaders.set(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains'
  );
  
  // ... rest of handler
}
5

Rate Limiting

Implement rate limiting to prevent abuse:
// app/utils/rateLimit.server.ts
import {RateLimiterMemory} from 'rate-limiter-flexible';

const rateLimiter = new RateLimiterMemory({
  points: 100, // Number of requests
  duration: 60, // Per 60 seconds
});

export async function checkRateLimit(identifier: string) {
  try {
    await rateLimiter.consume(identifier);
    return true;
  } catch {
    return false;
  }
}
6

Input Validation

Validate all user inputs using Zod:
import {z} from 'zod';

const subscriptionSchema = z.object({
  shop: z.string().min(1),
  contractId: z.string().min(1),
  // ... other fields
});

export async function action({request}: ActionFunctionArgs) {
  const formData = await request.formData();
  const data = Object.fromEntries(formData);
  
  // Validate input
  const validated = subscriptionSchema.parse(data);
  
  // Use validated data
}

Monitoring and Logging

Application Monitoring

1

Error Tracking

Integrate error tracking service:Sentry (Recommended):
pnpm add @sentry/remix
// app/entry.server.tsx
import * as Sentry from '@sentry/remix';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0,
});
2

Application Logging

The app uses Pino for structured logging:
// app/utils/logger.server.ts
import pino from 'pino';

export const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  // Production: JSON logs
  // Development: Pretty-printed logs
});
Configure log aggregation:
  • Datadog: Structured JSON logs
  • LogDNA: Cloud log management
  • CloudWatch: AWS logging
  • Cloud Logging: GCP logging
3

Uptime Monitoring

Set up uptime monitoring:Monitor endpoints:
  • GET /services/ping - Health check endpoint
  • GET /webhooks/cli - Webhook endpoint
4

Performance Monitoring

Track application performance:
  • Request/response times
  • Database query performance
  • Memory usage
  • CPU utilization
  • Error rates
Tools:
  • New Relic: APM platform
  • Datadog APM: Full-stack monitoring
  • Scout APM: Simple performance monitoring

Health Check Endpoint

The app includes a health check endpoint:
// app/routes/services.ping.tsx
export async function loader() {
  // Check maintenance mode
  if (process.env.MAINTENANCE_MODE === 'true') {
    throw new Response('Service Unavailable', {status: 503});
  }
  
  return json({status: 'ok'}, {status: 200});
}
Use for:
  • Load balancer health checks
  • Uptime monitoring
  • Deployment verification

Continuous Deployment

GitHub Actions Example

Create .github/workflows/deploy.yml:
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8
      
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      
      - name: Run type check
        run: pnpm run type-check
      
      - name: Run tests
        run: pnpm test
        env:
          NODE_ENV: test
      
      - name: Build application
        run: pnpm run build
        env:
          NODE_ENV: production
      
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v20
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'

Production Checklist

Before going live:

Infrastructure

  • Production database configured with backups
  • Environment variables set in hosting provider
  • SSL certificate configured and valid
  • Domain DNS configured correctly
  • CDN configured (if applicable)

Application

  • NODE_ENV=production set
  • Database migrations run successfully
  • Build completes without errors
  • All tests passing
  • Type checking passes

Shopify Configuration

  • App URLs updated in Partner Dashboard
  • OAuth redirect URLs configured
  • Webhooks verified and working
  • App tested with live Shopify store
  • Subscription flows tested end-to-end

Security

  • HTTPS enabled and enforced
  • Security headers configured
  • Secrets stored securely
  • Database uses SSL connections
  • Rate limiting implemented
  • Input validation in place

Monitoring

  • Error tracking configured (Sentry, etc.)
  • Logging configured and working
  • Uptime monitoring active
  • Performance monitoring enabled
  • Alerts configured for critical issues

Documentation

  • Deployment runbook created
  • Incident response plan documented
  • Team has access to production systems
  • Rollback procedure documented

Rollback Procedure

If deployment issues occur:
1

Identify the Issue

Check monitoring tools:
  • Error tracking dashboard
  • Application logs
  • Uptime monitors
2

Quick Rollback

Revert to previous deployment:Vercel:
# Promote previous deployment
vercel rollback
Railway:
  • Go to Deployments → Select previous deployment → Redeploy
Heroku:
heroku releases:rollback
3

Verify Rollback

  • Check health endpoints
  • Test critical user flows
  • Monitor error rates
4

Post-Mortem

  • Document what went wrong
  • Identify root cause
  • Update deployment checklist
  • Implement additional safeguards

Scaling Considerations

Horizontal Scaling

  • Deploy multiple instances behind a load balancer
  • Use managed database with connection pooling
  • Implement Redis for session storage (alternative to Prisma)
  • Use CDN for static assets

Background Jobs

For production-scale job processing:
  • Use Google Cloud Tasks (included in app)
  • Alternative: BullMQ with Redis
  • Alternative: AWS SQS
  • Alternative: Inngest

Database Optimization

  • Enable connection pooling (PgBouncer, ProxySQL)
  • Add database indexes for frequently queried fields
  • Implement read replicas for scaling reads
  • Use database query optimization tools

Support and Maintenance

Regular Maintenance Tasks

  • Update dependencies monthly: pnpm update
  • Review and rotate secrets quarterly
  • Check and update SSL certificates before expiry
  • Review application logs weekly
  • Test backup restoration monthly
  • Review and optimize database queries

Staying Updated

  • Monitor Shopify API changelog for breaking changes
  • Subscribe to hosting provider status pages
  • Watch for security advisories in dependencies
  • Keep Node.js version updated to LTS releases

Next Steps

Your app is now deployed! Consider:
  1. Setting up staging environment for testing
  2. Implementing feature flags for gradual rollouts
  3. Creating automated tests for critical paths
  4. Building monitoring dashboards
  5. Documenting operational procedures
For App Store submission, ensure your app provides value beyond the reference implementation and meets Shopify’s uniqueness requirements.

Build docs developers (and LLMs) love