Introduction: Why TypeScript and Kubernetes are the Perfect Pair
\nModern web development requires not only high-quality code but also reliable infrastructure. TypeScript, as a statically typed superset of JavaScript, allows you to create scalable server-side applications with minimal errors at the compilation stage. Kubernetes (K8s) is the de facto standard for container orchestration, providing automatic deployment, scaling, and management of applications.
\nIn this guide, we will practically walk through how to build a simple TypeScript application (Express API), package it into a Docker image, and deploy it in a Kubernetes cluster. You will get ready-made commands, configuration files, and an explanation of each step. The material is intended for developers who are already familiar with the basics of TypeScript and want to learn devops processes.
\nBy the end of the article, you will be able to independently deploy TypeScript projects into a production environment using Kubernetes.
\n\n1. Preparing the TypeScript Application for Containerization
\n1.1. Creating a Basic Express Server in TypeScript
\nFor demonstration, let's create a minimal API in TypeScript. Make sure you have Node.js and npm installed.
\nmkdir ts-k8s-app\ncd ts-k8s-app\nnpm init -y\nnpm install express\nnpm install -D typescript @types/node @types/express ts-node nodemon\nCreate tsconfig.json:
{\n "compilerOptions": {\n "target": "ES2020",\n "module": "commonjs",\n "outDir": "./dist",\n "rootDir": "./src",\n "strict": true,\n "esModuleInterop": true\n },\n "include": ["src/**/*"]\n}\nNow let's write the server itself. Create the file src/index.ts:
import express, { Request, Response } from 'express';\n\nconst app = express();\nconst PORT = process.env.PORT || 3000;\n\napp.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', timestamp: new Date().toISOString() });\n});\n\napp.get('/', (_req: Request, res: Response) => {\n res.send('Hello from TypeScript on Kubernetes!');\n});\n\napp.listen(PORT, () => {\n console.log(`Server running on port ${PORT}`);\n});\nAdd scripts to package.json:
"scripts": {\n "build": "tsc",\n "start": "node dist/index.js",\n "dev": "nodemon src/index.ts"\n}\nCheck the build: npm run build && npm start. The server should start on port 3000.
1.2. Optimization for Production
\nFor production, it is important to minimize the image size. Add .dockerignore:
node_modules\ndist\n.git\n*.md\n.env\nMake sure all dependencies are listed in dependencies (not in devDependencies), if they are needed at runtime.
2. Building a Docker Image for the TypeScript Application
\n2.1. Writing the Dockerfile
\nWe use a multi-stage build to reduce the size of the final image.
\n# Stage 1: Build\nFROM node:18-alpine AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\nRUN npm run build\n\n# Stage 2: Production\nFROM node:18-alpine\nWORKDIR /app\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/package.json ./\nEXPOSE 3000\nCMD ["node", "dist/index.js"]\nKey points:
\n- \n
npm ci— fast and deterministic dependency installation. \n - The second layer contains only the compiled JS and necessary modules. \n
- The base image
node:18-alpineweighs only ~120 MB. \n
2.2. Building and Checking the Image
\ndocker build -t ts-k8s-app:latest .\ndocker run -p 3000:3000 ts-k8s-app:latest\nOpen http://localhost:3000/health — you should see a JSON response. If in