The Complete Guide to Dockerizing TanStack Start Applications
From the Docker Captain - Kristiyan Velkov
Docker has become the de facto standard for deploying modern web applications, and TanStack Start is no exception. As a Docker Captain and contributor to the official Docker guides for React.js, Angular, Vue.js, and Node.js, I’ve spent countless hours optimizing containerization strategies for frontend frameworks. Today, I’m sharing everything you need to know about deploying TanStack Start applications with Docker—from basic setup to production-ready configurations.
❤️ If you found this guide helpful and like what I’m doing give a ⭐️ and support my open source projects.
🔗 Front-end Production files - you can find the real example how to dockernize all modern Front-end applications. Including TanStack Start example!
TanStack Start ?
TanStack Start is a powerful full-stack React framework that combines server-side rendering, API routes, and modern React features. Like any full-stack application, it requires careful deployment considerations.
🔗 Learn more: https://tanstack.com/start/latest
Step 1: The Foundation: A Production-Ready Dockerfile for TanStack Start
Let’s start with the Dockerfile—the blueprint for your container image. I’ve refined this configuration through dozens of production deployments, and it incorporates best practices that many developers overlook.
# =========================================
# Stage 1: Build the TanStack Start Application
# =========================================
ARG NODE_VERSION=24.12.0-slim
FROM node:${NODE_VERSION} AS builder
WORKDIR /app
# Copy package files first for optimal caching
COPY package.json package-lock.json* yarn.lock* pnpm-lock.yaml* ./
# Install dependencies with cache mounts for speed
RUN --mount=type=cache,target=/root/.npm \
--mount=type=cache,target=/usr/local/share/.cache/yarn \
--mount=type=cache,target=/root/.local/share/pnpm/store \
if [ -f package-lock.json ]; then \
npm ci --no-audit --no-fund; \
elif [ -f yarn.lock ]; then \
corepack enable yarn && yarn install --frozen-lockfile --production=false; \
elif [ -f pnpm-lock.yaml ]; then \
corepack enable pnpm && pnpm install --frozen-lockfile; \
else \
echo "No lockfile found." && exit 1; \
fi
COPY . .
# Build with cache mount for .vinxi artifacts
RUN --mount=type=cache,target=/app/.vinxi/cache \
if [ -f package-lock.json ]; then \
npm run build; \
elif [ -f yarn.lock ]; then \
yarn build; \
elif [ -f pnpm-lock.yaml ]; then \
pnpm build; \
else \
echo "No lockfile found." && exit 1; \
fi
# =========================================
# Stage 2: Run the TanStack Start Server
# =========================================
FROM node:${NODE_VERSION} AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOST=0.0.0.0
COPY --from=builder /app/.output ./
USER node
EXPOSE 3000
ENTRYPOINT ["node", "server/index.mjs"]
Understanding Multi-Stage Builds
This Dockerfile uses a two-stage build pattern, which is crucial for production images. Here’s why it matters:
Stage 1 (Builder): This stage contains everything needed to compile your application—Node.js, package managers, source code, and build tools. It’s large (often 1GB+) because it includes development dependencies.
Stage 2 (Runner): This stage contains only the compiled .output directory and a minimal Node.js runtime. The final image is typically 100-150MB—a 90% size reduction.
The benefit isn’t just storage. Smaller images mean faster deployments, reduced attack surface, and lower bandwidth costs when scaling horizontally.
The Power of Cache Mounts
Notice the --mount=type=cache directives. This is a BuildKit feature that persists package manager caches between builds:
RUN --mount=type=cache,target=/root/.npm \
npm ci
Even when Docker rebuilds a layer (like when you add a new dependency), the downloaded packages remain cached. In my testing, this reduces build times by 50-80% for large projects. It’s the difference between a 5-minute build and a 1-minute build.
Step 2: The .dockerignore File: Your First Line of Defense
Just as important as the Dockerfile is what you exclude from it. A comprehensive .dockerignore file speeds up builds and improves security:
# =============================================
# TanStack Start Production .dockerignore
# Optimized for minimal build context & security
# =============================================
# -----------------------------------------
# Dependencies (installed fresh in Docker)
# -----------------------------------------
node_modules/
.npm/
.pnpm-store/
.yarn/
# -----------------------------------------
# Build Outputs (regenerated during build)
# -----------------------------------------
.output/
.nitro/
.vinxi/
dist/
build/
out/
# -----------------------------------------
# Vite & Bundler Cache
# -----------------------------------------
.vite/
.vitepress/
*.tsbuildinfo
.turbo/
.parcel-cache/
# -----------------------------------------
# Test & Coverage Files
# -----------------------------------------
coverage/
__tests__/
*.test.ts
*.test.tsx
*.spec.ts
*.spec.tsx
vitest.config.*
jest.config.*
cypress/
playwright/
playwright-report/
test-results/
# -----------------------------------------
# Development & Debug Files
# -----------------------------------------
*.log
*.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.eslintcache
# -----------------------------------------
# Environment & Secrets (SECURITY)
# -----------------------------------------
.env
.env.*
!.env.example
*.pem
*.key
*.crt
secrets/
.secrets/
# -----------------------------------------
# IDE & Editor Files
# -----------------------------------------
.idea/
.vscode/
*.swp
*.swo
*~
*.sublime-*
# -----------------------------------------
# OS Generated Files
# -----------------------------------------
.DS_Store
.DS_Store?
._*
Thumbs.db
ehthumbs.db
Desktop.ini
# -----------------------------------------
# Version Control
# -----------------------------------------
.git/
.gitignore
.gitattributes
.github/
.gitlab/
.hg/
.svn/
# -----------------------------------------
# Docker Files (not needed in build context)
# -----------------------------------------
Dockerfile*
docker-compose*.yml
compose*.yml
.dockerignore
# -----------------------------------------
# Task Runners & Build Scripts
# -----------------------------------------
Makefile
Taskfile.yml
Taskfile.yaml
Justfile
# -----------------------------------------
# Documentation (not needed in production)
# -----------------------------------------
README.md
README*
CHANGELOG*
CONTRIBUTING*
LICENSE*
docs/
*.md
# -----------------------------------------
# AI & Development Tools
# -----------------------------------------
*.ai
*.aider*
*.chatgpt*
.cursor/
.copilot/
__pycache__/
openai/
kiro/
anthropic/
ai_outputs/
ai_cache/
.claude/
# -----------------------------------------
# Misc Development Files
# -----------------------------------------
*.map
*.d.ts.map
.editorconfig
.prettierrc*
.eslintrc*
eslint.config.*
prettier.config.*
tsconfig.*.json
!tsconfig.jsonI’ve seen production images accidentally ship with .env files containing database credentials or API keys. Always exclude sensitive files at the Docker level—it’s your last line of defense before secrets end up in a container registry.
Step 3: Building and Running Your Container
With your Dockerfile and .dockerignore in place, building is straightforward:
# Build the image
docker build -t my-tanstack-app .
# Run the container
docker run -p 3000:3000 my-tanstack-app
# Run in detached mode (background)
docker run -d -p 3000:3000 --name tanstack-app my-tanstack-app
🔗 Visit http://localhost:3000 to verify everything works. If you see your application, congratulations—you’ve successfully containerized a TanStack Start app.
Step 4 (Optional): Docker Compose for Complete Environments
For local development and single-host deployments, Docker Compose simplifies container orchestration. Create a compose.yml file:
services:
tanstack-start-app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_VERSION: 24.12.0-slim
image: tanstack-start-image
container_name: tanstack-start-container
environment:
NODE_ENV: production
PORT: 3000
HOST: 0.0.0.0
ports:
- '3000:3000'
restart: unless-stopped
Essential commands:
# Build and start (see logs in terminal)
docker compose up --build
# Build and start in background
docker compose up -d --build
# View logs from running container
docker compose logs -f
# Stop and remove containers
docker compose down
Security Best Practices
The Dockerfile includes several security measures worth highlighting:
Non-Root User Execution
USER node
This line ensures your application runs as an unprivileged user. Even if an attacker exploits a vulnerability, they can’t modify system files or install packages. It’s a simple directive with enormous security implications.
Minimal Base Image
Using node:24.12.0-slim instead of the full node:24 image removes unnecessary packages and documentation. The slim variant is ~400MB smaller and has fewer potential vulnerabilities.
Keep it mind that I used Node.js LTS version of the image tag, by the time you are reading this the Node.js LTS can be changed, so don’t forget to update it.
Real-World Example Repository
All the configurations in this guide are battle-tested in production environments. You can find complete working examples, including TanStack Start and other popular frameworks, in my open-source repository:
🔗 github.com/kristiyan-velkov/frontend-prod-dockerfiles
This repository contains production-ready Dockerfiles for:
TanStack Start
React.js applications
Angular projects
Vue.js applications
Node.js services
And more
Each example includes optimized Dockerfiles, .dockerignore files, and Docker Compose configurations you can use immediately.
Final Thoughts
Containerizing TanStack Start applications doesn’t have to be complicated. With the right Dockerfile structure, proper layer caching, and attention to security, you can create production-ready containers that are fast, secure, and portable.
The key principles to remember:
Use multi-stage builds to minimize final image size
Order layers strategically to maximize cache efficiency
Leverage cache mounts for faster dependency installation
Run as non-root user for security
Never bake secrets into image layers
Whether you’re deploying to AWS, Google Cloud, Azure, or your own infrastructure, these patterns will serve you well. Docker’s consistency guarantee means you can focus on building features instead of debugging environment-specific issues.
If you found this guide helpful and like what I’m doing give a ⭐️ to frontend-prod-dockerfiles repository and support me. 👨💻
The Docker community thrives on shared knowledge, and I’m always eager to learn new optimization techniques.
Happy containerizing!
Kristiyan Velkov is a Docker Captain and contributor to official Docker documentation for React.js, Angular, Vue.js, Node.js, and other popular frameworks. He specializes in frontend performance optimization and production deployment strategies.



Absolutely stellar guide on containerization best practices. The cachee mount optimization is something I started using after getting burned by ridiculously long rebuild times on large projects, and the difference is night and day. One thing that's interesting is how much the layer ordering can impact CI/CD pipelines when teams don't think about cache invalidation strategically.