The T3 Stack combines Next.js, TypeScript, tRPC, Prisma, Tailwind CSS, and NextAuth into a full-stack TypeScript application with end-to-end type safety. This guide covers deploying a T3 application on a VPS with proper production configuration.
Project Setup
# Create T3 app
npm create t3-app@latest my-t3-app
cd my-t3-app
# Select features during setup:
# TypeScript, tRPC, Prisma, Tailwind CSS, NextAuth
tRPC Router Example
// src/server/api/routers/post.ts
import { z } from "zod";
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
export const postRouter = createTRPCRouter({
getAll: publicProcedure.query(async ({ ctx }) => {
return ctx.db.post.findMany({
orderBy: { createdAt: "desc" },
include: { author: { select: { name: true } } },
});
}),
getById: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ ctx, input }) => {
return ctx.db.post.findUnique({ where: { id: input.id } });
}),
create: protectedProcedure
.input(z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.post.create({
data: {
title: input.title,
content: input.content,
authorId: ctx.session.user.id,
},
});
}),
});
Client Usage (Fully Type-Safe)
// src/pages/index.tsx
import { api } from "~/utils/api";
export default function Home() {
const { data: posts, isLoading } = api.post.getAll.useQuery();
const createPost = api.post.create.useMutation({
onSuccess: () => utils.post.getAll.invalidate(),
});
const utils = api.useUtils();
if (isLoading) return <div>Loading...</div>;
return (
<div className="container mx-auto p-4">
<h1 className="text-3xl font-bold">Posts</h1>
{posts?.map((post) => (
<div key={post.id} className="border p-4 rounded mt-4">
<h2 className="text-xl font-semibold">{post.title}</h2>
<p className="text-gray-600">by {post.author.name}</p>
</div>
))}
</div>
);
}
Environment Variables
# .env
DATABASE_URL="postgresql://user:pass@localhost:5432/t3app"
NEXTAUTH_SECRET="your-nextauth-secret"
NEXTAUTH_URL="https://myapp.example.com"
DISCORD_CLIENT_ID="your-discord-client-id"
DISCORD_CLIENT_SECRET="your-discord-secret"
Building for Production
# Run Prisma migrations
npx prisma db push
# Or for production migrations
npx prisma migrate deploy
# Build Next.js
npm run build
# Start production server
npm start
Docker Deployment
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN npx prisma generate
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
USER node
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
Systemd Service
[Unit]
Description=T3 Stack Application
After=network.target postgresql.service
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/t3app
ExecStart=/usr/bin/npm start
EnvironmentFile=/opt/t3app/.env
Environment=NODE_ENV=production
Restart=always
[Install]
WantedBy=multi-user.target
Nginx Configuration
server {
listen 443 ssl http2;
server_name myapp.example.com;
ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
location /_next/static/ {
proxy_pass http://127.0.0.1:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}
Summary
The T3 Stack delivers end-to-end type safety from database to UI with zero runtime overhead. tRPC eliminates the API integration layer entirely — change a server procedure, and TypeScript catches client-side breakages immediately. Prisma provides type-safe database access, and NextAuth handles authentication. For TypeScript-first full-stack applications, the T3 Stack on a VPS provides the most productive and maintainable development experience available.