Deploy Remix Apps with DeployHQ
Prerequisites:
- Node.js 18 or later
- SSH access to your server
- A DeployHQ account
What is Remix?
Remix is a React framework focused on web standards and progressive enhancement. It uses server-side rendering, nested routing with data loading, and form handling that works without JavaScript — enhancing progressively when JS is available.
As of 2025, Remix has merged with React Router v7 and uses Vite as its build tool.
Step 1: Create a Remix Project
npx create-remix@latest my-app
cd my-app
npm run dev
The app runs at http://localhost:5173.
Step 2: Project Structure
my-app/
├── app/
│ ├── root.tsx # Root layout
│ ├── entry.client.tsx # Client entry
│ ├── entry.server.tsx # Server entry
│ └── routes/ # File-based routing
│ ├── _index.tsx # → /
│ ├── about.tsx # → /about
│ └── blog.$slug.tsx # → /blog/:slug
├── public/ # Static assets
├── build/ # Build output
│ ├── server/ # Server bundle
│ └── client/ # Client assets
├── vite.config.ts
├── package.json
└── .env
Step 3: Core Features
Loaders (Server-Side Data Fetching)
// app/routes/blog.$slug.tsx
import type { LoaderFunctionArgs } from '@remix-run/node'
import { json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
export async function loader({ params }: LoaderFunctionArgs) {
const post = await db.posts.findUnique({ where: { slug: params.slug } })
if (!post) throw new Response('Not Found', { status: 404 })
return json({ post })
}
export default function BlogPost() {
const { post } = useLoaderData<typeof loader>()
return <article><h1>{post.title}</h1><div>{post.content}</div></article>
}
Actions (Form Handling)
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData()
const email = formData.get('email') as string
await subscribe(email)
return json({ success: true })
}
Nested Routes and Layouts
Routes can be nested — child routes render inside parent layouts via <Outlet />:
routes/
├── dashboard.tsx # Layout for /dashboard/*
├── dashboard._index.tsx # → /dashboard
├── dashboard.settings.tsx # → /dashboard/settings
└── dashboard.profile.tsx # → /dashboard/profile
Step 4: Deploy with DeployHQ
Build Command
cd %path% && npm ci && npm run build
Build output:
- build/server/ — Node.js server bundle
- build/client/ — Static client assets
PM2 Configuration
// ecosystem.config.cjs
module.exports = {
apps: [{
name: 'remix-app',
script: 'node_modules/.bin/remix-serve',
args: './build/server/index.js',
instances: 'max',
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 3000,
},
max_memory_restart: '512M',
}]
}
Post-Deployment SSH Command
cd /var/www/remix-app && npm ci --omit=dev && pm2 reload ecosystem.config.cjs --env production
Nginx Reverse Proxy
server {
listen 443 ssl http2;
server_name example.com;
# Serve static client assets directly
location /build/ {
alias /var/www/remix-app/build/client/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Proxy all other requests to Remix server
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Environment Variables
Set on the server in .env:
bash
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
SESSION_SECRET=a-long-random-string
Step 5: Troubleshooting
"Cannot find module" After Deployment
Ensure npm ci --omit=dev runs in the deployment directory. Remix's server bundle may reference packages that need to be installed on the server.
Hydration Mismatches
Ensure server and client render the same content. Common cause: using Date.now() or browser-only APIs in components without checking typeof window.
Port Conflicts
ss -tulnp | grep :3000
pm2 delete remix-app && pm2 start ecosystem.config.cjs --env production
Conclusion
Remix's web-standards approach and progressive enhancement make it a robust framework for server-rendered React applications. DeployHQ handles the build pipeline and PM2 process management for zero-downtime deployments.
Sign up for DeployHQ — free for one project.
Questions? Contact support@deployhq.com or on Twitter/X.