Dockerizing Legacy Applications
Eventually, you will need to transition your legacy application to a containerized environment.

Getting Started
To work with Docker, you need to set up a development environment. First, you’ll need to install Docker itself. Installation steps vary, depending on your operating system.
For this tutorial, we will use Ubuntu 24.04 as our operating system. Ubuntu is a popular choice for web hosting due to its stability, user-friendly interface, and extensive community support.
(Note, the Docker install for ARM servers is slightly different. You need to use this GPT Key:
echo "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
)
Installing Docker
- SSH to your vps and update your existing list of packages:
sudo apt update
sudo apt upgrade -y
2. Install required packages for Docker:
sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
3. Add Docker’s official GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
4. Add the Docker APT repository:
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
5. Update the package database with Docker packages from the newly added repository:
sudo apt update
6. Install Docker:
sudo apt install docker-ce -y
Installing Docker Compose
- Download the current stable release of Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
2. Apply executable permissions to the binary:
sudo chmod +x /usr/local/bin/docker-compose
3. Verify that the installation was successful:
docker-compose --version

Preparing for migration.
The Migration.
Example Migration: Legacy web app

Dockerfiles
Dockerfiles are scripts used to build Docker images. Imagine these files as a set of instructions you would follow to set up your environment on a VPS after installing the host OS (like Ubuntu, CentOS, etc.). Docker images follow the same concept. With a Dockerfile, you create an image for your lightweight server, known as a container.
The Importance of Layer Order in a Dockerfile
The order in which instructions are written in a Dockerfile is crucial for several reasons:
1. Cache Efficiency
Docker uses a layer caching mechanism to speed up the build process. Each instruction in a Dockerfile creates a new layer in the image, and Docker caches these layers. If you modify a layer, Docker must rebuild that layer and all subsequent layers. Therefore, placing instructions that change less frequently earlier in the Dockerfile can significantly improve build times.
2. Dependency Management
The order of layers can affect how dependencies are managed and how errors are propagated. For example, installing base packages or dependencies early in the Dockerfile ensures they are available for later stages of the build process. This is crucial for maintaining a stable and functional build environment.
3. Build Performance
By ordering instructions logically, you can optimize the build performance. For instance, placing instructions that add or modify application code towards the end of the Dockerfile minimizes the number of layers that need to be rebuilt when the code changes. This results in faster iteration times during development.
4. Image Size
The size of the final Docker image can be affected by the order of the instructions. Combining related operations into single instructions and ordering them efficiently can help reduce the number of layers and the overall image size. This is important for reducing storage costs and improving the performance of pulling and deploying images.
Example
Here is an example to illustrate the importance of order in a Dockerfile:
# Start with the base image
FROM node:14
# Install dependencies
COPY package.json /app/
WORKDIR /app
RUN npm install
# Copy application code
COPY . /app
# Set environment variables
ENV NODE_ENV production
# Expose the port the app runs on
EXPOSE 3000
# Start the application
CMD ["node", "server.js"]
In this example:
- Base Image: The base image is specified first, setting up the initial environment.
- Dependencies:
package.json
is copied andnpm install
is run early to leverage layer caching. If the dependencies inpackage.json
do not change, Docker can use the cached layer even if the application code changes. - Application Code: The application code is copied after installing dependencies. This way, changes to the application code do not invalidate the cached layers related to dependencies.
- Environment Variables and Configuration: These are set towards the end, ensuring they are applied to the final stages of the build.
- Expose and Command: These instructions come last to finalize the container configuration.
By understanding and utilizing the importance of layer order in a Dockerfile, you can create more efficient, maintainable, and performant Docker images.
The Dockerfile
FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && \ apt-get upgrade -y && \ apt-get install -y apache2 php php-dev php-mysql libapache2-mod-php php-curl php-json php-common php-mbstring composer software-properties-common && \ a2enmod rewrite
COPY ./php.ini /etc/php/7.2/apache2/php.ini COPY ./site.conf /etc/apache2/sites-available/site.conf COPY ./apache2.conf /etc/apache2/apache2.conf
RUN rm -rfv /etc/apache2/sites-enabled/*.conf && \ ln -s /etc/apache2/sites-available/site.conf /etc/apache2/sites-enabled/site.conf
EXPOSE 80 EXPOSE 443
CMD ["apachectl", "-D", "FOREGROUND"]
Breakdown:
1. Base Image
FROM ubuntu:latest
- Uses the latest version of Ubuntu as the base image.
2. Environment Variable
ENV DEBIAN_FRONTEND=noninteractive
- Sets the
DEBIAN_FRONTEND
environment variable tononinteractive
to suppress interactive prompts during package installations, which is required for automatic builds and prevents build failure.
3. Package Updates and Installations
RUN apt-get update -y && \ apt-get upgrade -y && \ apt-get install -y apache2 php php-dev php-mysql libapache2-mod-php php-curl php-json php-common php-mbstring composer software-properties-common && \
a2enmod rewrite
- Updates the package list and upgrades installed packages.
- Installs Apache, PHP, and a variety of PHP extensions.
- The
a2enmod rewrite
command is used in Apache web server configurations to enable themod_rewrite
module.
4. Configuration Files
COPY ./php.ini /etc/php/7.2/apache2/php.ini COPY ./site.conf /etc/apache2/sites-available/site.conf COPY ./apache2.conf /etc/apache2/apache2.conf
- Copies custom configuration files for PHP and Apache into the appropriate directories.
5. Apache Site Configuration
RUN rm -rfv /etc/apache2/sites-enabled/*.conf && \ ln -s /etc/apache2/sites-available/site.conf /etc/apache2/sites-enabled/site.conf
- Removes existing site configuration files and creates a symbolic link to the custom site configuration file.
6. Expose required ports and starting Apache
EXPOSE 80
EXPOSE 443CMD ["apachectl", "-D", "FOREGROUND"]
- Exposes HTTP and HTTPS ports.
- Defines the command to start Apache in the foreground when the container runs.
Building our Docker image
To build a Docker image from the Dockerfile, you can use two common methods: building with the Docker CLI and using Docker Compose.
Here is an explanation of both methods:
Method 1: Building with the Docker CLI
- Ensure Docker is Installed: Make sure Docker is installed on your system. You can verify this by running
docker --version
in your terminal. - Navigate to the Directory: Open your terminal and navigate to the directory containing your Dockerfile using the
cd
command. - Build the Docker Image: Use the
docker build
command to create the Docker image. You can specify a tag for your image with the-t
flag.
docker build -t my-apache-php-image .
In this command:
-t my-apache-php-image
tags the image with the namemy-apache-php-image
.- The
.
at the end specifies the current directory as the build context, which should contain the Dockerfile.
Method 2: Building with Docker Compose
- Ensure Docker and Docker Compose are Installed: Verify that both Docker and Docker Compose are installed on your system. You can check their versions using
docker --version
anddocker-compose --version
. - Create a
docker-compose.yml
File: In the same directory as your Dockerfile, create adocker-compose.yml
file with the following content:

- In this file:
version: '3'
specifies the version of the Docker Compose file format.services
defines the services that will run. In this case, there’s a single service namedweb
.build: .
indicates that the Dockerfile in the current directory should be used to build the image.ports
maps port 80 and 443 on the host to the container.
- Build and Run the Docker Image: Use the
docker-compose up
command to build the image and start the container:
docker-compose up --build
The --build
flag ensures the image is built before starting the container.