如何使用 Docker 和 Bun 部署 TanStack Start
Source: Dev.to

TanStack Start 正在迅速成为希望获得 TanStack Router 强大功能并具备全栈能力的开发者的首选框架。它前沿、速度快,开发者体验(DX)令人惊叹。
但说实话:从笔记本上使用 npm run dev 迁移到健壮的容器化生产环境,往往是头疼的开始。
在本指南中,我们不仅仅是让它“跑起来”。我们将使用 Docker 和 Bun 构建一个 高性能部署流水线。我们会实现一个自定义资源服务器,将文件缓存到 RAM、处理压缩,并以闪电般的速度提供你的应用。
0️⃣ 前置条件(不要跳过!)
在我们动手编辑任何 Dockerfile 之前,有一个关键要求。
根据官方 TanStack Start 文档,使用 Bun 部署目前需要 React 19。如果你的项目仍在使用 React 18,服务器可能会崩溃或表现异常。
请先升级你的依赖:
bun install react@19 react-dom@19
准备好了吗?让我们开始构建。
1️⃣ 基础:Vite 配置
首先,我们需要确保 Vite 已经为生产环境做好准备。我们希望路径别名(如 @/components)能够正常工作,并且去除 console 日志,以保持生产日志的整洁。
vite.config.ts
// vite.config.ts
import { defineConfig } from "vite";
import { devtools } from "@tanstack/devtools-vite";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import viteReact from "@vitejs/plugin-react";
import viteTsConfigPaths from "vite-tsconfig-paths";
import tailwindcss from "@tailwindcss/vite";
import { fileURLToPath, URL } from "node:url";
export default defineConfig({
esbuild: {
// Clean up logs in production
drop: ["console", "debugger"],
},
server: {
port: 3058,
},
plugins: [
devtools(),
// This plugin is vital for resolving paths like "@/lib/utils"
viteTsConfigPaths({
projects: ["./tsconfig.json"],
}),
tailwindcss(),
tanstackStart(),
viteReact(),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
2️⃣ 秘密酱料:自定义 Bun 服务器
这是最酷的部分。我们不再运行 node server.js,而是使用一个利用 Bun 原生速度的自定义脚本。该脚本充当智能资源服务器:它将小文件(图标、CSS、细小的 JS 片段)直接加载到内存(RAM)中,自动进行 Gzip 压缩,并生成 ETag,以实现完美的浏览器缓存。
为什么?
- 速度 – RAM 比磁盘更快。
- 压缩 – Gzip 自动处理。
- 效率 – ETag 让浏览器完美缓存资源。
在项目根目录创建一个名为 server.ts 的文件(请参阅 TanStack Router 仓库中的完整参考实现),并粘贴以下代码:
// server.ts
import path from "node:path";
// --- Configuration ---
const SERVER_PORT = Number(process.env.PORT ?? 3000);
const CLIENT_DIRECTORY = "./dist/client";
const SERVER_ENTRY_POINT = "./dist/server/server.js";
// Simple Logging Utility
const log = {
info: (msg: string) => console.log(`[INFO] ${msg}`),
error: (msg: string) => console.error(`[ERROR] ${msg}`),
success: (msg: string) => console.log(`[SUCCESS] ${msg}`),
};
// --- Asset Preloading Logic ---
// 5 MiB limit for in‑memory files
const MAX_PRELOAD_BYTES = Number(
process.env.ASSET_PRELOAD_MAX_SIZE ?? 5 * 1024 * 1024
);
// (Helper functions for ETag, Gzip, and Glob patterns omitted for brevity
// but ensure you include the full logic from the source provided earlier!)
/**
* The Main Server Initializer
*/
async function initializeServer() {
log.info("Starting Production Server...");
// 1. Load the TanStack Start handler
let handler: { fetch: (request: Request) => Response | Promise };
try {
const serverModule = (await import(SERVER_ENTRY_POINT)) as any;
handler = serverModule.default;
log.success("TanStack Start handler initialized");
} catch (error) {
log.error(`Failed to load handler: ${String(error)}`);
process.exit(1);
}
// 2. Initialise static routes (scans ./dist/client)
// Note: In your full file, ensure you include the `initializeStaticRoutes` function!
const { routes } = await initializeStaticRoutes(CLIENT_DIRECTORY);
// 3. Start Bun server
const server = Bun.serve({
port: SERVER_PORT,
routes: {
// Serve cached assets first
...routes,
// Fallback to the App handler for everything else
"/*": (req: Request) => {
return handler.fetch(req);
},
},
});
log.success(`Server listening on http://localhost:${String(server.port)}`);
}
initializeServer().catch((err) => {
log.error(String(err));
});
// ... (Paste the rest of the helper functions: initializeStaticRoutes, etc. here)
重要提示: 使用官方材料中的完整
server.ts代码,以获得完整的缓存优势。
3️⃣ Dockerfile:小巧高效
我们将使用 多阶段构建 来保持最终镜像体积极小。(Dockerfile 的其余内容遵循相同的模式——复制构建好的资源,仅安装生产依赖,并运行 Bun 服务器。)
# ---- Builder Stage ----
FROM oven/bun:1 AS builder
WORKDIR /app
# Install dependencies (including React 19)
COPY package.json bun.lockb ./
RUN bun install --production=false
# Copy source files
COPY . .
# Build the client and server bundles
RUN bun run build
# ---- Runtime Stage ----
FROM oven/bun:1-alpine AS runtime
WORKDIR /app
# Copy only the built output and production deps
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json .
COPY --from=builder /app/bun.lockb .
# Install only production dependencies
RUN bun install --production
# Expose the port the server listens on
EXPOSE 3000
# Start the Bun server
CMD ["bun", "run", "server.ts"]
为什么这个 Docker 设置有效
我们保持最终的 Docker 镜像体积小,因为我们不把构建工具(TypeScript、Vite 等)放入生产容器——只包含运行应用所需的内容。
关键技术细节
Vite 需要在构建时获取环境变量(例如你的 API URL),以便将它们“烘焙”进客户端 JavaScript。请特别注意 Dockerfile 中的 ARG 部分。
Dockerfile(多阶段)
# syntax=docker/dockerfile:1
# =============================================
# Stage 1: Builder
# =============================================
FROM oven/bun:1-alpine AS builder
WORKDIR /app
# Install dependencies (cached layer)
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
# Copy the source code
COPY . .
# --- IMPORTANT ---
# Vite needs these variables AT BUILD TIME to replace import.meta.env values
ARG VITE_API_URL
ARG VITE_BETTER_AUTH_URL
ENV VITE_API_URL=${VITE_API_URL}
ENV VITE_BETTER_AUTH_URL=${VITE_BETTER_AUTH_URL}
# Build the app
RUN bun run build
# =============================================
# Stage 2: Runner ( Production )
# =============================================
FROM oven/bun:1-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Install curl for healthchecks
RUN apk add --no-cache curl
# Copy package files
COPY package.json bun.lock ./
# Install ONLY production deps (react, etc.)
RUN bun install --production
# Copy the build artifacts from the Builder stage
COPY --from=builder --chown=bun:bun /app/dist ./dist
COPY --from=builder --chown=bun:bun /app/server.ts ./server.ts
USER bun
# The port is dynamic via Env Vars
ENV PORT=3058
EXPOSE 3058
# Start the custom Bun server
CMD ["bun", "server.ts"]
4️⃣ 编排:Docker Compose
使用 Docker Compose 可以轻松管理环境变量、端口和健康检查。
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
# Pass these to the Builder stage!
VITE_BETTER_AUTH_URL: ${VITE_BETTER_AUTH_URL}
VITE_API_URL: ${VITE_API_URL} # (optional, if needed at build time)
environment:
# Runtime config for the server
NODE_ENV: production
PORT: ${PORT:-3058}
# App secrets & configs
VITE_API_URL: ${VITE_API_URL}
DATABASE_URL: ${DATABASE_URL}
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
# SMTP / Email config
HOST_EMAIL: ${HOST_EMAIL}
HOST_PORT: ${HOST_PORT}
HOST_AUTH_USER: ${HOST_AUTH_USER}
HOST_AUTH_PASS: ${HOST_AUTH_PASS}
ports:
# Map host port to container port
- '${PORT:-3058}:${PORT:-3058}'
restart: always
healthcheck:
# Ping the server to ensure it's alive
test: ['CMD-SHELL', 'curl -fsS http://localhost:${PORT:-3058} || exit 1']
interval: 10s
timeout: 5s
retries: 5
结论
您不再在容器内部运行开发服务器。此设置为您提供了一个 高度优化、内存缓存、基于 Bun 的生产环境。
快速回顾
- React 19 – Bun 开发的必备版本
- server.ts – 提供高级缓存和 gzip 压缩
- Dockerfile – 多阶段构建,正确处理构建时环境变量
- Docker Compose – 通过健康检查编排运行时
- 创建一个包含所需变量的
.env文件。 - 运行:
docker compose up --build -d
- 观看您的 TanStack Start 应用飞速运行!
欲了解更多详情,请访问我的 blog。
