f in x
Docker and Containerization — From Prototyping to Production for Italian SMEs
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

Docker and Containerization — From Prototyping to Production for Italian SMEs

[2026-06-23] Author: Ing. Calogero Bono

If you're still configuring development environments manually on every machine, if your deployments require a one-hour checklist, if "it works on my machine" is a phrase you hear too often — then you have a problem that Docker and containerization solve. We at Meteora Web have seen it dozens of times: projects slowed down by version conflicts, servers with outdated dependencies, clients waiting days for a fix. Docker is not just a trend: it's an operational paradigm shift that transforms how you develop, test and deploy. And because we come from accounting, we measure it in terms of time saved, reduced infrastructure costs, and lower error rates.

What are Docker and containerization and why should an SME care?

Docker is a platform that lets you run applications in containers: lightweight, isolated environments that bundle everything needed (code, runtime, libraries, environment variables). Unlike virtual machines, containers share the host OS kernel, starting from minimal images. This means a container is a few megabytes and starts in seconds.

For an Italian SME, the benefit is immediate: reproducibility. If your developer works on Windows, your staging server on Ubuntu, and production on Debian, Docker ensures the environment is identical everywhere. Goodbye "it works on my machine". Goodbye endless configurations of Apache, PHP, MySQL every time a new team member joins. We've been using it since 2018 and cut project setup times from days to minutes.

How does it differ from a virtual machine?

A VM includes a full operating system (kernel included) — gigabytes, dedicated resources. A container shares the host kernel: it's lighter, faster to start, consumes less RAM and CPU. For a business with limited budget, this means running more services on the same server without buying additional hardware. We often see clients moving from 3 VMs to a single server with 5 containers, saving hundreds of euros per month in hosting.

# Check Docker is installed
docker --version
# Basic command: run an isolated container
docker run hello-world

How to build an optimized Dockerfile for PHP/Laravel applications?

The heart of containerization is the Dockerfile: a set of instructions that describes how to build the image. For a Laravel app, we want a lightweight, secure, and fast-to-rebuild image. Best practices are: start from an official base, use multi-stage builds to separate build and production, leverage layer caching, don't install unnecessary packages.

Sponsored Protocol

Practical example: multi-stage for Laravel

# Stage 1: Composer dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-scripts --no-progress

# Stage 2: Node build (if needed)
FROM node:18 AS frontend
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn production

# Stage 3: Production
FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo_mysql bcmath
COPY --from=vendor /app/vendor /var/www/vendor
COPY --from=frontend /app/public /var/www/public
COPY . /var/www

# Final optimizations
RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache
USER www-data
CMD ["php-fpm"]

This Dockerfile is lightweight (~150 MB), secure (no SSH keys exposed), and leverages layer caching: if you change only code, dependencies are not rebuilt. Important: order of commands matters – copy files that change less often first (composer.json, package.json) to maximize cache.

Common mistakes

  • Copying the entire project immediately: each modification invalidates subsequent layer caches.
  • Using latest images: prefer specific versions (e.g., php:8.2-fpm-alpine) for reproducibility.
  • Leaving credentials in the Dockerfile: use environment variables passed at runtime.

Immediate action: open your PHP/Laravel project, create a Dockerfile following the multi-stage pattern, and test with docker build -t myapp .

Does Docker Compose really simplify multi-container local development?

Yes. In a real environment you never have just a web server: there are databases, caches, queues, mailcatcher. Managing them manually with docker run is chaotic. Docker Compose lets you define all containers, their relationships, variables, and volumes in a single YAML file. With one command docker compose up -d your entire stack is up.

Docker Compose for Laravel + MySQL + Redis

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    volumes:
      - .:/var/www
    depends_on:
      - mysql
      - redis
    environment:
      DB_HOST: mysql
      REDIS_HOST: redis

  mysql:
    image: mysql:8.0
    ports:
      - "3307:3306"
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: laravel

  redis:
    image: redis:7-alpine

volumes:
  dbdata:

With this setup, your local development matches production. The volume mounted on app lets you modify code and see changes in real-time (if using a server with hot reload).
Immediate action: install Docker Compose (if you don't have it), copy the file above, run docker compose up -d and visit http://localhost:8080.

Sponsored Protocol

We at Meteora Web use Compose for WordPress and Node.js projects too: it reduces team conflicts and speeds up onboarding.

Docker Networking: how to connect containers in development and production?

Containers are isolated by default. To make them talk – e.g., the PHP app to MySQL – you must put them on the same Docker network. Docker provides network drivers: bridge (default), host, overlay (for swarm). For local development, the default bridge works fine; for multi-server production, use overlay.

Custom bridge network

# Create a network
 docker network create mynet
# Start two containers on the same network
docker run -d --network mynet --name db mysql:8.0
docker run -d --network mynet --name app -p 80:80 myapp:latest
# Now app can resolve db by hostname 'db'

This is exactly what Docker Compose does automatically: it creates a default network for all services. In production, for security, define separate networks: one for the public frontend, one for the internal backend. Remember not to expose ports on internal services (e.g., MySQL) – only what's needed.

Immediate action: in your docker-compose.yml, add networks: with two networks: frontend and backend. Block external database access.

Docker Volumes: where to store data to never lose it?

A container is ephemeral: if you delete it, you lose its data. Docker volumes are persistent directories managed by the Docker engine, independent of the container lifecycle. Use them for databases, uploads, logs, and any data that must survive restarts or updates.

Types of volumes

  • Named volumes: managed by Docker (docker volume create mydata). Ideal for databases.
  • Bind mounts: mount a host directory into the container. Useful for development (sharing code).
  • tmpfs: mount files in RAM – very fast but volatile. For cache or temporary sessions.

Example: volume for MySQL

services:
  mysql:
    image: mysql:8.0
    volumes:
      - dbdata:/var/lib/mysql
volumes:
  dbdata:

In production, back up volumes periodically. We use docker run --rm -v dbdata:/volume -v $(pwd):/backup alpine tar czf /backup/db_backup.tar.gz -C /volume . to export.

Sponsored Protocol

Immediate action: verify that all services that write data (DB, Redis, storage) have named volumes. Never use bind mounts for databases in production.

How to containerize a Laravel app with Docker for development and production?

We already covered the Dockerfile. For production, you need additional tweaks: minimize the image, run the container as non-root user, use environment variables for configuration, enable logging to stdout/stderr (Docker captures them automatically).

Production with Nginx + PHP-FPM

Often you use two containers: Nginx to serve static files (and proxy to PHP-FPM) and PHP-FPM to execute code. Here's a docker-compose for production:

version: '3.8'
services:
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www
    depends_on:
      - php
  php:
    build:
      context: .
      dockerfile: Dockerfile.prod  # optimized multi-stage image
    volumes:
      - .:/var/www
    environment:
      - APP_ENV=production
      - DB_HOST=mysql
      - ...
    depends_on:
      - mysql
  mysql:
    image: mysql:8.0
    volumes:
      - dbdata:/var/lib/mysql

This stack is scalable, secure, and easy to update. Immediate action: separate your Dockerfile into two (dev and prod) and create an nginx.conf that points to php:9000.

Docker for Node.js and React: multi-stage configuration examples

For full-stack applications with Node.js backend (Express, Nest, Next.js) and React frontend, containerization follows the same logic: multi-stage to reduce final size. For Next.js, you can leverage static build or runtime Node.

Dockerfile for a Next.js app

# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build

# Production stage
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["yarn", "start"]

Note: for development, use a bind mount volume for hot reload. We recommend separating the dev image (with nodemon) from the production one.

Sponsored Protocol

Immediate action: create a Dockerfile for your Node.js project, use multi-stage and check the size with docker images.

Private image registry: when is it needed and how to manage it with Harbor?

When you start distributing images across teams or to production, public Docker Hub is not enough: pull limits, security concerns, need for audit. A private registry lets you host your images in a controlled repository, with vulnerability scanning and access control. Harbor is a powerful open-source solution, with a web interface, replication across regions, and integration with AD/LDAP.

Install Harbor with Docker Compose

Harbor itself is installed via Docker Compose. Download the official installer, configure harbor.yml, run ./install.sh and you have a full registry with scanning based on Trivy.

# Push an image to private registry
docker tag myapp:latest myregistry.example.com/myapp:1.0
docker push myregistry.example.com/myapp:1.0
# Pull from a remote server
docker pull myregistry.example.com/myapp:1.0

Immediate action: if you manage more than 3 containers in production, consider adopting a private registry. Harbor also provides replication for disaster recovery.

Docker Scout: keep vulnerabilities under control

Security is something we at Meteora Web take very seriously. Many Italian SMEs underestimate vulnerabilities in Docker images: an outdated base image can expose the entire stack. Docker Scout is a service integrated in Docker Desktop and CLI that analyzes images and reports CVEs with remediation advice.

Using Docker Scout

# Analyze a local image
docker scout quickview myapp:latest
# Get recommendations to update packages
docker scout recommendations myapp:latest

Scout compares libraries against vulnerability databases (NVD, GitHub Advisory) and provides a rating: "Critical", "High", "Medium". We run it in CI/CD: if an image has critical vulnerabilities, we block the deploy.

Immediate action: in your CI workflow (GitHub Actions, GitLab CI), add a step that runs docker scout quickview and fails if critical CVEs exist. Example:

- name: Scan image
  run: |
    docker scout quickview myapp:latest --exit-code --only-severity critical

Official docs: Docker Scout docs

Sponsored Protocol

From Docker Compose to Kubernetes: when is it time to migrate?

Docker Compose is perfect for single-host environments or small clusters. But when your application grows, containers multiply, you need automatic scaling, rolling updates, advanced health checks – then Kubernetes becomes necessary. There's no fixed rule, but we see three indicators:

  • You need horizontal scaling of services based on load.
  • You want zero-downtime updates (Canary, Blue-Green).
  • You run multiple environments (staging, production) on multi-server clusters.

First steps toward K8s

Start with a local cluster (minikube or kind) to learn the concepts: Pod, Deployment, Service, Ingress. Convert your docker-compose.yml into Kubernetes YAML manifests. Use tools like Kompose (auto-converter) but review the output manually.

# Install Kompose
curl -L https://github.com/kubernetes/kompose/releases/latest/download/kompose-linux-amd64 -o kompose
chmod +x kompose
sudo mv kompose /usr/local/bin/
# Convert docker-compose.yml
kompose convert -f docker-compose.yml
# Apply manifests
kubectl apply -f .

Kubernetes has a learning curve, but the return in terms of reliability and automation is huge. We at Meteora Web have accompanied clients from single containers on VPS to Kubernetes clusters on cloud (DigitalOcean, AWS), reducing downtime from hours to seconds.

Immediate action: install minikube, run minikube start and deploy your application with a simple Deployment and Service.

What to do now

You have the foundations to transform your workflow. Here are 5 concrete actions to start today:

  1. Install Docker Desktop (or Docker Engine on Linux) and verify with docker run hello-world.
  2. Create a Dockerfile for your main project – start with a simple static web app to get comfortable.
  3. Set up Docker Compose for local development with database and cache – notice the difference in setup time.
  4. Run a security audit with docker scout on your current images and fix critical vulnerabilities.
  5. Evaluate if Kubernetes is for you: start with minikube and try to deploy a containerized version.

Remember: a container is not an end in itself – it's a tool to reduce costs, increase reliability, and speed up development. We at Meteora Web use it every day and measure it in saved revenue.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()