Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk11 min

How to Build and Run a Node.js Application with Docker on a VPS

A production-minded walkthrough for containerizing Node.js, installing Docker on Ubuntu, running Compose on a VPS, securing traffic with Nginx and Certbot, and protecting data with restart policies and tested backups.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The reliable production pattern is to build an immutable Node.js image, run it with Docker Compose on an Ubuntu VPS, keep the application port private, and put Nginx on the public edge for HTTP and HTTPS. Docker packages the Node runtime, your code, dependencies, configuration, and supporting files into a portable image, so the same artifact can be tested in CI and run on the server. The VPS should expose only SSH, HTTP, and HTTPS; Node can listen on port 3000 inside the container while Nginx forwards approved requests to it.

This guide takes you from a new VPS to a repeatable deployment with non-root execution, health checks, restart behavior, TLS renewal, firewall controls, rollback, and independent backups.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Understand the production layout

A request should travel through a small, deliberate chain:

  1. A DNS A or AAAA record points your domain to the VPS.
  2. Nginx listens publicly on ports 80 and 443, redirects HTTP to HTTPS, terminates TLS, and applies proxy headers.
  3. Nginx forwards the request over a private path to the Node.js container on port 3000.
  4. The container serves the application as a non-root user and exposes a cheap health endpoint such as /healthz.
  5. Persistent state lives in a named volume or an external database/object-storage service, not in the container’s writable layer.

Docker’s own description is useful here: “Containerizing your application means packaging it together with its dependencies, configuration, and runtime into a single portable unit called a container image.” The image is replaceable; data and secrets must be managed separately.

#1 Best Overall
Sale
TOZO A1 Wireless Earbuds Bluetooth 5.3 Light Weight in Ear Headphones | IPX5 Waterproof,2 Mics for AI Calls,Immersive Premium Sound Bass Headset with Charging Case,32 EQ via App Black
  • [Ultra-Lightweight Ear Buds Designed for Small Ears] Each earbud weighs only 3.7g and features a compact, ergonomic in-ear design made especially for small ears. Secure, low-profile, and comfortable for workouts, all-day wear or overnight sleeping.The ultra-slim profile sits flush in your ear with no hard edges poking or rubbing, so you can even sleep on your side without discomfort.
  • [Immersive Stereo Sound with TOZO OrigX Technology] TOZO OrigX tuning delivers clear vocals, balanced mids, and natural stereo sound for music, podcasts, and videos.
  • [Long Battery Life for Daily Use] Get up to 7 hours of playtime on a single charge, with up to 32 hours total using the charging case—ideal for workdays, commuting, and extended listening sessions.
  • [Bluetooth 5.3 & Stable Connection] Bluetooth 5.3 provides fast pairing, stable wireless performance, and reduced dropouts as you move around home or office.
  • [Deep Bass with Clear Vocals] High-performance drivers produce punchy bass while keeping vocals clean and detailed for everyday listening.

Why the application port stays private

Publishing port 3000 directly to the internet gives attackers a second path around Nginx, TLS policy, request limits, and access logs. With host Nginx, bind the container only to the loopback interface (for example, 127.0.0.1:3000:3000). With Nginx in another container, put both services on the same Docker network and proxy to the service name. A Compose expose entry alone is not reachable by a host-installed Nginx; use a loopback binding or a shared Docker network.

Prepare the VPS before installing software

Choose a current supported Ubuntu LTS image and record the details that affect compatibility and recovery: VPS region, CPU architecture, memory, disk size, IPv4 and IPv6 addresses, and the provider’s snapshot policy. Confirm that your Node dependencies and any native modules support that architecture.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Harden the initial account

  • Create a named, non-root account with sudo access.
  • Install your SSH public key for that account and disable password authentication after verifying a second session works.
  • Apply all Ubuntu updates and reboot if the kernel or critical services changed.
  • Decide where encrypted off-site backups will be stored before putting production data on the machine.
  • Reserve a DNS name for the application and note which person or system controls the DNS credentials.

Keep one root-capable recovery path through the VPS provider while you test SSH changes. Do not remove it until key-based login has been confirmed from a separate terminal.

Install Docker Engine from the Ubuntu repository

Use Docker’s official Ubuntu apt-repository procedure for production. Docker describes its convenience script as intended for testing and development; it can cause unexpected major upgrades, so do not pipe an unreviewed script into a shell on a production host.

  1. Update apt metadata and install the repository prerequisites, including ca-certificates and curl.
  2. Add Docker’s signing key and the repository entry for the exact Ubuntu release and CPU architecture.
  3. Refresh apt metadata again.
  4. Install docker-ce, docker-ce-cli, containerd.io, docker-buildx-plugin, and docker-compose-plugin.
  5. Check the daemon and run Docker’s smoke test:
sudo systemctl status docker
sudo docker run hello-world
docker version
sudo docker compose version

If the daemon is inactive, inspect sudo journalctl -u docker --no-pager -n 100 before changing configuration. You may add your deployment user to the docker group, but membership grants root-equivalent control of the host; use it only when that risk is acceptable, and otherwise keep using sudo docker.

Build a production-grade Node.js image

Pin a Node major and minor line that your application supports, commit that choice, and update it deliberately. The following multi-stage example uses Node 24 as an illustration, not as a universal recommendation. The build stage can contain compilers and development dependencies; the runtime stage contains only what is needed to serve the application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
Smart Watch for Men Women Compatible with iPhone Samsung Android Phone 1.69 inch Full Touch Screen IP68 Waterproof Bluetooh Fitness Tracker Heart Rate/Sleep Monitor (Black)
  • 【IP68 Waterproof Multifunctinal Smart Watch】Torjalph fitness activity tracker smart watch is IP68 waterproof technology ,you can wear it when swimming,handwashing,it's rain-proof which means that you perform multiple exercise modes outside without worrying about sweat or rain corroding your fitness smart watch,and this watch equipped with a 230 mAh large capacity battery magnetic charging ,and this sports activity tracker watch can be used 5-7 days If fully charged.
  • 【Larger 1.83'' Full Screen Watch & Multi-dial Selection】1.83 inch full HD color touch screen smart watch for men / women provides excellent 240*284 resolution image quality ,This Bluetooth smart watch offers a better touch experience and remarkable immersion and ,more than 200 watch faces for you to choose from App"Da Fit ", you can change the dial style according to your mood to wear keeping the world on your wrist
  • 【Accurate Activity Tracker Smart Watch 】sports watch activity tracker uses sweat and dust resistant breathable silicone strap, our Fitness Tracker Smart Watch for Android iOS Phones supports 12 sports modes,walking,running,bicycle,swimming,badminton,basketball,football,rope skipping. This bluetooth calls smart watch for men women is lightweight and stylish, bringing more convenient and relaxing sports experience while also providing more protection.
  • 【Your 24 Hour Personal Daily Health Monitor】Fitness watch for women men full touch screen IP67 waterproof bluetooth smartwatch for Android iOS Phones with heart rate blood pressure monitor multifunction Andriod smart watch with steps counter sleep monitor sports fitness watch, monitoring blood oxygen saturation allows you to understand your physical condition in real time to track fitness progress ,to know your health status in time.
  • 【More Functions Bluetooth Call Smart Watch】This fitness activity smart watch full screen for iPhone and Android phones includes a bunch of new features including:The bluetooth smart watch can hang up the phone, receive and display WhatsAPP /Text /Facebook /Twitter /Messenger reminder.Support sedentary reminder, alarm clock, stopwatch, timer,weather information,music control, remote camera control compatible with Android 5.0 and above or iOS 9.0 and above smartphones.
FROM node:24-bookworm AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:24-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]

Change the build command and output directory to match your project. If the application is plain JavaScript, omit the build step and copy the source files needed at runtime. Your server must listen on 0.0.0.0 inside the container; listening only on 127.0.0.1 would make it inaccessible through Docker networking.

Keep the build context clean

Create a .dockerignore beside the Dockerfile:

node_modules
npm-debug.log
*.log
dist
coverage
.env
.env.*
.git
.DS_Store

Never bake production credentials into an image layer. Supply runtime secrets through the deployment environment or a dedicated secret manager. Image layers can remain available in registries and build caches even after a file is deleted in a later layer.

Build and test before pushing

docker build -t my-node-app:commit-id .
docker run --rm -p 127.0.0.1:3000:3000 --env NODE_ENV=production my-node-app:commit-id

Exercise /healthz and a representative API request locally, then stop the test container. In CI, scan the image, record the commit identifier, and push that immutable tag to your registry. Avoid using latest as the only deployment identifier because it does not tell you which source revision is running or make rollback deterministic.

Run the app with Docker Compose on the VPS

Place a version-controlled compose.yaml and a server-only .env.production in a protected deployment directory. This example assumes Nginx runs on the host, so the app is bound only to loopback. If Nginx runs in Docker, replace the loopback port mapping with a shared network and use the service name as its upstream.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
services:
  app:
    image: registry.example.com/my-node-app:${APP_TAG}
    restart: unless-stopped
    env_file: .env.production
    ports:
      - '127.0.0.1:3000:3000'
    volumes:
      - app-data:/app/data
    healthcheck:
      test: ['CMD', 'node', '-e', "require('http').get('http://127.0.0.1:3000/healthz', r => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s

volumes:
  app-data:

restart: unless-stopped restarts the container after a process exit or Docker daemon restart, while respecting an intentional manual stop. The health check should be cheap and independent of slow third-party calls; use it to keep deployment automation from switching traffic to a process that has started but is not ready.

Protect the environment file

sudo chown root:root .env.production
sudo chmod 600 .env.production

Include only runtime configuration there: database connection details, cookie signing keys, API credentials, and the selected APP_TAG. Keep the file out of source control and back it up through your secret-management process, not by copying it into a public repository.

Rank #3
New Bee Guitar Cable 10ft Electric Instrument Bass AMP Cord
  • [Low Noise High Fidelity] New bee guitar cord with 22AWG oxygen-free-copper center conductors provides enhanced signal transfer, the PVC insulation material and the high-density stranded copper double shield provide noise rejection for quiet operation
  • [Ideal for Choice] New bee 1/4 straight and right angle metal connectors, ensures a secure connection in any instrument, pedal, or amp jack. L shape great fro on stage guitar stands
  • [Wide Compatible] Suitable for mixer, power amplifier, microphone, effector, decoder, equalizer, electoronic organ, electric guitar, electric bass and other electronic equipments. Widely used in the installation of smart home, karaoke speaker, professional stage audio, etc
  • [Easy Disassemble] Making incidental repairs quick and easy! Very nice, flexible cable
  • [Dont Hesite to Order] 24 hours customer services and professional technology team are standing by

Start and inspect the stack

sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail=200 app

A container can be running while the application is failing on every request, so check both the Compose health state and the endpoint itself. From the VPS, test curl -f http://127.0.0.1:3000/healthz before configuring the public proxy.

Configure Nginx as the public edge

Install Nginx on the host and create a server block for the real domain. The upstream address below matches the loopback port mapping in the Compose file.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Use your distribution’s Nginx site-enable mechanism, validate the configuration with sudo nginx -t, and reload with sudo systemctl reload nginx. Keep the application port unpublished beyond loopback. If you need WebSockets, retain the upgrade headers; otherwise they are harmless for ordinary HTTP requests.

Point DNS to the VPS and enable HTTPS

Verify DNS first

Create the required A record for IPv4 and, only when the VPS is correctly configured for IPv6, an AAAA record. Confirm both names resolve to the intended addresses before requesting a certificate. A stale AAAA record can make some clients reach a different server even when IPv4 testing succeeds.

Run Certbot’s Nginx integration

  1. Connect over SSH with sudo privileges and install Certbot using the current Ubuntu instructions; those instructions recommend the snap package.
  2. Run sudo certbot --nginx, select the domain names, and allow the HTTP-to-HTTPS redirect when prompted.
  3. Verify the resulting HTTPS site in a browser and with an HTTP client.
  4. Test automated renewal: sudo certbot renew --dry-run.

DNS API credentials are highly sensitive: anyone who obtains them can change records and take over the site. Store them as secrets, restrict their permissions, and do not put them in the web directory or image.

Apply firewall and Docker network controls

Allow SSH only from trusted addresses where practical, and allow TCP 80 and 443 at the edge. Do not allow a database port or the Node port from the internet. Docker warns that published ports can bypass ordinary ufw or firewalld expectations because Docker installs its own forwarding rules.

Rank #4
BSIMB 15.6" Digital Family Calendar & Chore Chart, No Subscription Fees
  • Digital Family Calendar: BSIMB digital calendar wall touch screen puts an end to your chaotic life. Designed for busy families, the digital calendar keeps you organised and in step with your family. Assign different colours and tasks to each family member to make family scheduling easier and more intuitive.
  • Easy Setup and Auto-Sync: Plug in, connect to WLAN and auto-syn your electric calendar in minutes. Digital wall calendar compatible with Google calendar, iCloud calendar, Outlook calendar, Cozi calendar and Yahoo calendar. Add events directly on the device or through the ecalendar app. Electronic calendar stays current whether you're at work or traveling—no manual updates needed.
  • All-In-One Home Assistant: Smart calendar builds habits and encourage independence with personalised chores. Display daily meal plan to reduce the daily ‘What's for dinner?’ stress. To-do lists keep track of everything you want. Turning chores into valuable achievements with stars and rewards, making life easier and more enjoyable.
  • Manage Anytime Anywhere: BSIMB electronic calendar for family stay connected with the powerful ecalendar app. Access and manage your digital wall calendar anytime, anywhere with free features like stars & rewards, photo screensaver, chore charts, meals plans, to-do lists etc. Electronic planner paid features like Magic Import tailor advanced tools for users with special needs.
  • Stay in Touch with Loved Ones: Share photos or videos from your phone to electronic wall calendar with app, instantly update your life moment, and keep in touch with your family, especially those who are far away; you can also surprise your smart calendar digital wall by sending him a wish card; it's also great for keeping tech-illiterate elders in touch with the youngsters.

Review the documented Docker firewall model and place host policy in the DOCKER-USER chain when you need to restrict forwarded container traffic. Test from an external network that only the intended ports answer, and test the VPS address directly to ensure port 3000 is not reachable. A loopback-only binding is a strong default, but it should still be verified after every Compose change.

Deploy by immutable tag, then verify

  1. Build and scan the image in CI.
  2. Tag it with the source commit ID and push that exact tag to your registry.
  3. Set APP_TAG in .env.production on the VPS.
  4. Run sudo docker compose pull app && sudo docker compose up -d app.
  5. Inspect sudo docker compose ps and sudo docker compose logs --tail=200 app.
  6. Check the health endpoint locally and then make a representative request through the HTTPS domain.
  7. Record the deployed tag and keep the previous tag and known-good environment file available.

If the new release fails, set APP_TAG back to the previous commit and run the same pull and up command. This is a deterministic rollback; rebuilding on the server can produce a different artifact because package registries, compiler versions, or source files may have changed.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Persist data and design real backups

Container replacement deletes the container’s writable layer. Put uploads, generated assets, and other state that must survive replacement in the named volume, or use an external managed database and object store. Docker volumes persist independently of a container, but persistence is not backup.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Back up according to the data type

  • Use database-aware dumps for relational or document databases rather than copying files while the database is writing.
  • Copy volume snapshots to storage outside the VPS and encrypt them in transit and at rest.
  • Keep more than one recovery point and document retention and deletion rules.
  • Perform scheduled restore drills to a clean volume or staging VPS; a backup that has never been restored is unverified.
  • Include the Compose file, image tag history, migration scripts, and secret-recovery procedure in the disaster-recovery runbook.

Provider snapshots are useful for rapid machine recovery, but they are not a substitute for an independently stored backup. A snapshot can share the same account, region, failure domain, or accidental-deletion risk as the original VPS.

Choose the operating model deliberately

Decision Option A Option B What changes operationally
Image strategy Single-stage image Multi-stage image Single-stage is simpler to understand; multi-stage keeps compilers and development dependencies out of the runtime image and usually reduces its contents.
Traffic edge Nginx on the host Proxy container Host Nginx has a direct relationship with the VPS firewall and system service manager; a proxy container gives stack-level isolation but requires a shared network and proxy-specific certificate handling.
State location Local named volumes Managed database or object storage Volumes can be fast and simple but leave backup, durability, and recovery work on you; managed services shift more of that operational burden away from the VPS.
Release method Pull a registry image Build on the VPS Registry pulls make the tested artifact reproducible and rollback quick; server builds use local bandwidth and tools but can drift from CI.
Supervision Docker restart policy External service manager Docker restart policies cover container exits and daemon restarts; an external manager can supervise the Docker Compose command itself when the host requires a broader service contract.

Operate and update the deployment safely

Schedule a maintenance window for base-image and operating-system updates when the application cannot tolerate a restart. Pull a newly rebuilt image rather than modifying a running container. Review logs for startup failures, authentication errors, failed migrations, and repeated restarts, but avoid logging passwords, tokens, session cookies, or full request bodies.

Keep the Node version, package lockfile, Dockerfile, Compose file, Nginx configuration, and deployment scripts under reviewable version control. When upgrading Node or a native dependency, build and exercise the image in CI on the same CPU architecture as the VPS when possible. Test migrations against a backup or staging copy before running them against production data.

Troubleshoot common failures

The container exits immediately

Run sudo docker compose logs --tail=200 app and inspect the command, working directory, required environment variables, and built output path. An image that contains dist/index.js but starts server.js will exit before Nginx can connect. A missing native library or incompatible CPU architecture appears in the startup log.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Nginx returns 502 Bad Gateway

First test curl -f http://127.0.0.1:3000/healthz on the VPS. If it fails, fix the container or its port binding. If it succeeds, check the Nginx upstream address, configuration syntax, and reload status. A host Nginx cannot reach a container that has only expose and no host binding; use the loopback mapping shown above or move Nginx into the Docker network.

The app works inside Docker but not through Nginx

Confirm that Node listens on 0.0.0.0, not only on the container loopback interface. Check the Host and X-Forwarded-Proto headers if the framework generates redirects or secure cookies. Verify that the Nginx server block’s server_name matches the requested hostname.

HTTPS issuance fails

Check that every requested DNS name resolves to this VPS from outside your network, port 80 is allowed, and Nginx responds for that hostname. Remove or correct an outdated AAAA record. Run Certbot again only after the plain-HTTP challenge path is reachable.

A container does not return after reboot

Confirm the Compose project was started with restart: unless-stopped, inspect sudo systemctl status docker, and run sudo docker compose ps. An intentional manual stop remains stopped under this policy; start it explicitly when you are ready.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Data disappeared after redeployment

Inspect the mounted volume with sudo docker inspect and confirm the new Compose file uses the same volume name and mount path. If the application wrote to another directory, move that path into a named volume before the next deployment. Restore a recent backup to a separate volume before attempting repairs on the only copy.

Production verification checklist

  • docker version and docker compose version show the expected installations.
  • sudo docker run hello-world succeeds during host setup.
  • The image runs as the intended non-root node user and sets NODE_ENV=production.
  • The health endpoint passes inside the container and through the HTTPS hostname.
  • External scans show only the intended SSH, HTTP, and HTTPS exposure; no database or Node port is public.
  • sudo certbot renew --dry-run completes successfully.
  • A forced container exit demonstrates the selected restart behavior, while a manual stop remains stopped when unless-stopped is configured.
  • A recent backup restores successfully to a clean volume or staging VPS.
  • The current image tag, previous rollback tag, environment-file recovery method, and data-restore steps are written in the runbook.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.