Docs / Programming & Development / Deploy T3 Stack

Deploy T3 Stack

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 402 views · 4 min read

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.

Was this article helpful?