Back to Blog
Tutorial7 min read2026-05-01

How to Containerize an Existing Application with Docker

Step-by-step guide to containerizing an existing application — from writing your first Dockerfile to running it in production.

Why Containerize an Existing App?

Most containerization guides assume you're starting fresh. In reality, you're more likely to have an existing application — a Node.js API, a Python Flask service, a Ruby on Rails app — that you need to package into a Docker container so it can run consistently across environments and deploy to modern cloud platforms.

This guide walks you through the process systematically, covering the most common languages and pitfalls.

Step 1: Understand What Your App Needs

Before writing a Dockerfile, answer these questions:

  • What runtime does it need? (Node.js 20, Python 3.12, Ruby 3.3...)
  • What system dependencies does it have? (ImageMagick, libpq, ffmpeg...)
  • What ports does it listen on?
  • What environment variables does it expect?
  • Does it write to the local filesystem? (uploads, caches, logs)
  • How do you start it in production? (node server.js, gunicorn app:app...)

Document the answers — your Dockerfile is essentially the answer to all these questions in code.

Step 2: Choose a Base Image

Pick an official base image matching your runtime. Prefer Alpine variants for smaller images:

RuntimeRecommended Base Image
Node.jsnode:20-alpine
Pythonpython:3.12-alpine
Rubyruby:3.3-alpine
Gogolang:1.22-alpine (builder) + scratch (runtime)
PHPphp:8.3-fpm-alpine

Step 3: Write the Dockerfile

Node.js Application

FROM node:20-alpine

# Install system deps if needed (e.g., for canvas, sharp, etc.)
RUN apk add --no-cache libc6-compat

WORKDIR /app

# Copy and install dependencies first (cache optimization)
COPY package*.json ./
RUN npm ci --only=production

# Copy application source
COPY . .

# Create a non-root user and switch to it
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

EXPOSE 3000
CMD ["node", "server.js"]

Python (Flask/FastAPI) Application

FROM python:3.12-alpine

RUN apk add --no-cache gcc musl-dev libffi-dev

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

EXPOSE 8000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000", "--workers", "4"]

Step 4: Create .dockerignore

Always create this before your first build:

# Node.js
node_modules
npm-debug.log

# Python
__pycache__
*.pyc
.venv
venv

# Common
.git
.env
*.env
.DS_Store
*.log
coverage
.pytest_cache
dist
build

Step 5: Build and Test Locally

# Build the image
docker build -t my-existing-app:local .

# Run it with environment variables from your .env file
docker run -d   -p 3000:3000   --env-file .env   --name my-app-test   my-existing-app:local

# Check logs
docker logs -f my-app-test

# Test it
curl http://localhost:3000/health

Step 6: Handle Common Issues

"Module not found" / "Package not installed"

Your .dockerignore might be excluding files needed for the build, or you have platform-specific native modules compiled on your host.

# Rebuild inside the container to ensure platform compatibility
docker run --rm -v $(pwd):/app -w /app node:20-alpine npm ci

App can't connect to the database

Inside a container, localhost refers to the container itself, not your host machine. Use host.docker.internal (Docker Desktop) or a Docker Compose service name:

# Development: reach host machine's database
DATABASE_URL=postgres://user:pass@host.docker.internal:5432/mydb

File permission errors

When switching to a non-root user, ensure your app's directories are owned correctly:

RUN mkdir -p /app/uploads && chown appuser:appgroup /app/uploads
USER appuser

Step 7: Deploy to PandaStack

Once your container runs locally, push your code to GitHub and deploy:

npm install -g @pandastack/cli
panda login
panda deploy --type container --repo your-org/your-repo

PandaStack builds your image directly from the Dockerfile in your repository — no need to push to an external registry. Set your environment variables from [dashboard.pandastack.io](https://dashboard.pandastack.io) and connect to managed databases (PostgreSQL, MySQL, Redis, MongoDB) provisioned by the platform.

Your containerized app goes from a GitHub push to a live HTTPS endpoint. For the full deployment guide, visit [docs.pandastack.io](https://docs.pandastack.io).

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also