{"id":2285,"date":"2026-07-20T12:43:14","date_gmt":"2026-07-20T10:43:14","guid":{"rendered":"https:\/\/kindsonthegenius.com\/blog\/how-to-dockerize-angular-and-node-js-application\/"},"modified":"2026-08-26T21:14:27","modified_gmt":"2026-08-26T19:14:27","slug":"how-to-dockerize-angular-and-node-js-application","status":"publish","type":"post","link":"https:\/\/kindsonthegenius.com\/blog\/how-to-dockerize-angular-and-node-js-application\/","title":{"rendered":"How to Dockerize Angular and Node.js Application"},"content":{"rendered":"<p><!-- ktg-updated-banner --><\/p>\n<p><em>Updated August 2026 \u2014 full tutorial restored for this URL.<\/em><\/p>\n<p>In this tutorial we <strong>Dockerize<\/strong> a typical stack: <strong>Angular<\/strong> SPA served by Nginx, and a <strong>Node.js<\/strong> API \u2014 wired together with Compose.<\/p>\n<ol>\n<li><a href=\"#t1\">Project layout<\/a><\/li>\n<li><a href=\"#t2\">Node API Dockerfile<\/a><\/li>\n<li><a href=\"#t3\">Angular multi-stage + Nginx<\/a><\/li>\n<li><a href=\"#t4\">docker-compose<\/a><\/li>\n<li><a href=\"#t5\">API URL and CORS<\/a><\/li>\n<\/ol>\n<p><strong id=\"t1\">1. Project layout<\/strong><\/p>\n<pre><code>repo\/\n  api\/          # Express (or Nest) app\n  web\/          # Angular app\n  docker-compose.yml\n<\/code><\/pre>\n<p><strong id=\"t2\">2. Node API Dockerfile<\/strong><\/p>\n<pre><code>FROM node:20-alpine\nWORKDIR \/usr\/src\/app\nCOPY package*.json .\/\nRUN npm ci --omit=dev\nCOPY . .\nEXPOSE 3000\nCMD [\"node\", \"server.js\"]\n<\/code><\/pre>\n<p><strong id=\"t3\">3. Angular multi-stage + Nginx<\/strong><\/p>\n<pre><code>FROM node:20-alpine AS build\nWORKDIR \/app\nCOPY package*.json .\/\nRUN npm ci\nCOPY . .\nRUN npm run build -- --configuration=production\n\nFROM nginx:1.27-alpine\nCOPY nginx.conf \/etc\/nginx\/conf.d\/default.conf\nCOPY --from=build \/app\/dist\/web\/browser \/usr\/share\/nginx\/html\nEXPOSE 80\n<\/code><\/pre>\n<p>Example <code>nginx.conf<\/code> proxying API calls:<\/p>\n<pre><code>server {\n  listen 80;\n  root \/usr\/share\/nginx\/html;\n  location \/ {\n    try_files $uri $uri\/ \/index.html;\n  }\n  location \/api\/ {\n    proxy_pass http:\/\/api:3000\/;\n  }\n}\n<\/code><\/pre>\n<p><strong id=\"t4\">4. docker-compose<\/strong><\/p>\n<pre><code>services:\n  api:\n    build: .\/api\n    environment:\n      PORT: 3000\n  web:\n    build: .\/web\n    ports: [\"8080:80\"]\n    depends_on: [api]\n<\/code><\/pre>\n<p><strong id=\"t5\">5. API URL and CORS<\/strong><\/p>\n<p>Prefer same-origin <code>\/api<\/code> via Nginx so the browser never needs CORS for local Compose. If the SPA calls the API directly, set <code>CORS<\/code> on Express and use an environment-specific <code>environment.prod.ts<\/code> API base URL.<\/p>\n<p>Related: <a href=\"https:\/\/kindsonthegenius.com\/blog\/how-to-connect-node-js-to-postgresql-database-and-fetch-data-step-by-step\/\">Connect Node.js to PostgreSQL<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Updated August 2026 \u2014 full tutorial restored for this URL. In this tutorial we Dockerize a typical stack: Angular SPA served by Nginx, and a &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[35],"tags":[],"class_list":["post-2285","post","type-post","status-publish","format-standard","hentry","category-algorithms"],"acf":[],"_links":{"self":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2285","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/comments?post=2285"}],"version-history":[{"count":2,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2285\/revisions"}],"predecessor-version":[{"id":2451,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2285\/revisions\/2451"}],"wp:attachment":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/media?parent=2285"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/categories?post=2285"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/tags?post=2285"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}