Docker Compose facilitates the definition and management of multi-container Docker applications. It makes it easier to run these interconnected services, including a database, frontend, and backend API, by enabling their joint launch and control. Code that describes each service and its dependencies is written in a docker-compose.yml; once specified, a single docker-compose command can launch all services, simplifying the coordination of testing and development environments.
I personally prefer Compose because it is faster, simpler, and more repeatable than manually launching and joining containers, as it means your containers will always run with the same setup as specified in the docker-compose.yml. Also, Compose automatically sets up a Docker network so that your containers may talk to one another, and it controls your Docker storage volumes, automatically reattaching them upon the replacement or restart of a service.
I'll be explaining the steps I took to containerize my full-stack application using Docker Compose
1. CLONE AND SET-UP THE PROJECT'S REPOSITORY
First, clone the repository from GitHub by running this command
git clone https://github.com/ehisakhile/Dream-Vacation-App.git
cd Dream-Vacation-App/
Then open the project in VScode (if that's your preferred IDE)
code .
2. SETUP THE BACKEND AND FRONTEND
MySQL SetUp
Login to your MySQL via the command line
mysql -u root -p
Create the Database
CREATE DATABASE dreamvacations;
Backend SetUp
Change the directory to the backend folder and install all dependencies:
cd backend
npm install
Next, create an environment (.env) file in the backend directory with the following content:
PORT=3001
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=yourdbpassword
DB_NAME=dreamvacations
COUNTRIES_API_BASE_URL=https://restcountries.com/v3.1
Start the server
npm start
Frontend SetUp
Change directory into the frontend folder and install dependencies:
cd ../frontend
npm install
This should be in the .env file for the frontend directory:
PORT=3000
REACT_APP_API_URL=http://localhost:3001
Handle the legacy OpenSSL support by running this command:
export NODE_OPTIONS=--openssl-legacy-provider
After all that is completed, start the frontend:
npm start
Once that is successful, you should be able to access the frontend with the link
http://localhost/3000
and the backend
http://localhost/3001/api/destinations
3. DOCKERIZE THE APPLICATION
For this part we will be setting up docker files for the individual directories and docker-compose in the root directory.
Create a file named docker-compose.yml
in the root of your project, not inside backend or frontend.
Also create an init.sql file in the root directory (for MySQL database schema)
Docker-Compose file
services:
# MySQL Database
mysql:
image: mysql:8.0
container_name: dream-vacation-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: yourdbrootpassword
MYSQL_DATABASE: dreamvacations
MYSQL_USER: dreamuser
MYSQL_PASSWORD: yourdbpassword
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- dream-vacation-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
# Backend Service
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: dream-vacation-backend
restart: unless-stopped
ports:
- "3001:3001"
environment:
- PORT=3001
- DB_HOST=mysql
- DB_PORT=3306
- DB_USER=root
- DB_PASSWORD=yourdbpassword
- DB_NAME=dreamvacations
- COUNTRIES_API_BASE_URL=https://restcountries.com/v3.1
depends_on:
mysql:
condition: service_healthy
networks:
- dream-vacation-network
volumes:
- ./backend:/app
- /app/node_modules
# Frontend Service
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: dream-vacation-frontend
restart: unless-stopped
ports:
- "3000:3000"
environment:
- PORT=3000
- REACT_APP_API_URL=http://localhost:3001
- NODE_OPTIONS=--openssl-legacy-provider
depends_on:
- backend
networks:
- dream-vacation-network
volumes:
- ./frontend:/app
- /app/node_modules
stdin_open: true
tty: true
volumes:
mysql_data:
networks:
dream-vacation-network:
driver: bridge
The docker compose file defines the specifications for the Frontend, Backend API and MySQL. The Backend runs on port 3001, connected to MySQL. The Frontend will run on port 3000 with full API access. All services communicate via the Docker network.
Data is stored in Docker volumes so it persists even after a restart.
init.sql file
-- Initialize the dreamvacations database
USE dreamvacations;
-- Create the destinations table
CREATE TABLE IF NOT EXISTS destinations (
id INT AUTO_INCREMENT PRIMARY KEY,
country VARCHAR(255) NOT NULL,
capital VARCHAR(255),
population BIGINT,
region VARCHAR(255)
);
Next, we create separate DockerFile and .dockerignore files inside the /backend
and /frontend
directories
Backend DockerFile in backend/DockerFile
# Use Node.js 18 LTS as base image
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json (if available)
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Expose the port the app runs on
EXPOSE 3001
# Command to run the application
CMD ["npm", "start"]
.dockerignore file in backend/.dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.cache
.DS_Store
.log
Frontend DockerFile in frontend/DockerFile
# Use Node.js 18 LTS as base image
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package.json and package-lock.json (if available)
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Set the NODE_OPTIONS environment variable for legacy OpenSSL provider
ENV NODE_OPTIONS=--openssl-legacy-provider
# Expose the port the app runs on
EXPOSE 3000
# Command to run the application
CMD ["npm", "start"]
.dockerignore file in frontend/.dockerignore
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.cache
.DS_Store
*.log
Now we can proceed to build and run the application from docker
docker-compose up --build
Once it builds and runs successfully you can access the
frontend: http://localhost/3000
backend: http://localhost/3001/api/destinations
Conclusion
Utilising Docker Compose provides a powerful and streamlined approach to deploying full-stack applications, offering significant advantages over traditional deployment methods. This is evident in how containerization simplifies the complex orchestration of multiple services - from frontend frameworks and backend APIs to databases and caching layers - into a single, manageable configuration file.
The declarative nature of Docker Compose allows for the definition of the entire application stack as code, ensuring consistency across development, staging, and production environments. This approach eliminates the common "it works on my machine" problem while providing reproducible deployments that can be version-controlled and easily rolled back when needed.
The major benefits of this approach include simplified dependency management, where each service runs in its own isolated container with its specific requirements; scaling capabilities through service replication; and enhanced portability across different cloud providers. The networking and volume management features of Docker Compose further enable secure inter-service communication and persistent data storage without complex configuration overhead.
For part 2, I'll be deploying the application on AWS cloud infrastructure with Amazon RDS for MySQL.
Top comments (0)