Docker and TypeScript: Application Containerization for Developers

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Docker and TypeScript: Why Containerization is Needed

Modern web development is unthinkable without tools that ensure environment consistency. Docker has become the de facto standard for packaging, delivering, and running applications. Combined with TypeScript — a strictly typed language that compiles to JavaScript — containerization provides a powerful tool for creating scalable and reliable services.

Why containerize a TypeScript application? First, it solves the "it works on my machine" problem. Second, it simplifies deployment to production and CI/CD. Third, it allows isolating dependencies and Node.js versions. In this article, we will break down how to create a Docker image for a TypeScript application, configure multi-stage builds, and use docker-compose for local development.



Docker Basics for a TypeScript Developer

Before writing code, let's recall the basic concepts of Docker: an image is a template for creating a container; a container is a running instance of an image; a Dockerfile is the instruction set for building an image.

For a TypeScript application on Node.js, we will need:

  • Node.js runtime (usually the node:18-alpine or node:20-alpine image for minimal size)
  • Installing dependencies (npm install or yarn install)
  • Compiling TypeScript to JavaScript (tsc)
  • Running the compiled application

Let's look at a simple example of a TypeScript application (file src/index.ts):

import express from 'express';

const app = express();const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => { res.send('Hello from TypeScript + Docker!');});

app.listen(PORT, () => { console.log(`Server running on port ${PORT}`);});

And tsconfig.json:

{  "compilerOptions": {    "target": "ES2020",    "module": "commonjs",    "outDir": "./dist",    "rootDir": "./src",    "strict": true,    "esModuleInterop": true  },  "include": ["src"],  "exclude": ["node_modules", "dist"]}

Now let's create a Dockerfile for this application.



Creating a Dockerfile for a TypeScript Application

A basic Dockerfile might look like this:

FROM node:18-alpine AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build

FROM node:18-alpine AS runnerWORKDIR /appCOPY --from=builder /app/dist ./distCOPY --from=builder /app/package*.json ./COPY --from=builder /app/node_modules ./node_modulesEXPOSE 3000CMD ["node", "dist/index.js"]

Here, a multi-stage build is used. The first stage (builder) compiles TypeScript into JavaScript, the second (runner) contains only the compiled code and runtime dependencies. This reduces the size of the final image — TypeScript sources and dev dependencies do not end up in it.

Important: we run npm ci (clean install) instead of npm install for reproducibility and speed.



Optimizing the Dockerfile: Layer Caching

Docker caches each layer (instruction). To speed up the build, copy files that change infrequently earlier. For example, package.json and package-lock.json are copied before all source files, so code changes do not lead to reinstalling dependencies.

Additionally, you can add a .dockerignore:

node_modulesdist.git*.md.gitignore.env

This prevents unnecessary files from entering the build context.



docker-compose for Local Development

For convenient local development, use docker-compose.yml. It allows you to run the application with automatic reloading (using nodemon or

Blogs

Book Recommendations