DevOps  

How to Deploy a Full Stack Application Using Docker and GitHub Actions

Introduction

Deploying a full-stack application can feel complex, especially for beginners. You have frontend code, backend code, dependencies, servers, and deployment steps to manage. This is where Docker and GitHub Actions make life much easier.

In simple terms, Docker helps you package your application so it runs the same way everywhere, and GitHub Actions helps you automate deployment whenever you push code. Together, they enable you to deploy applications faster, with fewer errors and less manual work.

In this article, we will walk through deploying a full-stack application using Docker and GitHub Actions, explained in plain language with practical examples.

What Is a Full Stack Application?

A full-stack application usually has two main parts:

  • Frontend: The user interface, built using technologies like HTML, CSS, JavaScript, React, or Angular

  • Backend: The server-side logic, built using technologies like Node.js, .NET, Java, or Python

Example: An online shopping app where users browse products on the frontend, and the backend handles login, orders, and payments.

Why Use Docker for Deployment?

Docker allows you to package your application and all its dependencies into containers. This ensures that the app runs the same on your laptop, testing server, and production server.

Key benefits of Docker:

  • No "it works on my machine" problem

  • Easy to move applications between environments

  • Consistent and repeatable deployments

  • Better resource usage

Example: A backend API running in Docker will behave the same on any server that supports Docker.

What Is GitHub Actions?

GitHub Actions is a CI/CD tool built directly into GitHub. It allows you to automate tasks like building code, running tests, creating Docker images, and deploying applications.

In simple terms:

  • You push code to GitHub

  • GitHub Actions runs automated steps

  • Your application gets built and deployed automatically

This saves time and reduces human errors.

High-Level Deployment Flow

Before going into details, let us understand the overall flow.

  • Developer pushes code to GitHub

  • GitHub Actions workflow starts

  • Application is built using Docker

  • Docker image is pushed to a registry

  • Server pulls the image and runs the container

This entire process happens automatically once it is set up.

Step 1: Prepare the Full Stack Application

Your project should have a clear structure.

Example structure:

  • frontend folder

  • backend folder

  • Docker-related files

Make sure the frontend and backend can run independently before containerizing them.

Step 2: Create Dockerfile for Backend

A Dockerfile tells Docker how to build an image for your application.

Example backend Dockerfile:

FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["npm", "start"]

This file installs dependencies, copies code, and starts the backend server.

Step 3: Create Dockerfile for Frontend

The frontend also needs its own Dockerfile.

Example frontend Dockerfile:

FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

This builds and serves the frontend application.

Step 4: Use Docker Compose for Local Testing

Docker Compose helps run multiple containers together.

Example docker-compose file:

version: '3'
services:
  backend:
    build: ./backend
    ports:
      - "5000:5000"
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"

This allows you to test the full stack application locally using one command.

Step 5: Push Code to GitHub Repository

Once Docker works locally, push your code to a GitHub repository.

Make sure:

  • Dockerfiles are committed

  • Application runs without manual changes

This repository will be used by GitHub Actions for deployment.

Step 6: Create GitHub Actions Workflow

GitHub Actions workflows are defined using YAML files.

Example basic workflow:

name: Deploy Full Stack App

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Build Docker images
      run: docker-compose build

This workflow starts when code is pushed to the main branch.

Step 7: Push Docker Images to a Registry

To deploy, Docker images must be stored in a container registry.

Common options:

  • Docker Hub

  • Cloud container registries

Example steps include:

  • Login to registry

  • Build Docker image

  • Push image

This makes the image available for servers to pull.

Step 8: Deploy Containers on the Server

On the server:

  • Install Docker

  • Pull Docker images from registry

  • Run containers

Example:

docker pull your-image-name
docker run -d -p 80:3000 your-image-name

This starts the application on the server.

Step 9: Automate Deployment End to End

You can extend GitHub Actions to:

  • Connect to servers securely

  • Pull latest images automatically

  • Restart containers

This creates a full CI/CD pipeline where deployment happens automatically after every code push.

Common Mistakes to Avoid

Some common mistakes include:

  • Hardcoding secrets in code

  • Not testing Docker images locally

  • Skipping error handling in workflows

  • Using outdated images

Using environment variables and proper testing avoids these issues.

Benefits of Using Docker and GitHub Actions Together

Using Docker and GitHub Actions together provides:

  • Faster deployments

  • Consistent environments

  • Reduced manual work

  • Easy rollback and updates

This approach is widely used in modern DevOps practices.

Summary

Deploying a full stack application using Docker and GitHub Actions simplifies the entire deployment process. Docker ensures that your application runs consistently across environments, while GitHub Actions automates building, testing, and deployment. By combining both tools, developers can create reliable, scalable, and automated deployment pipelines that save time and reduce errors. This approach is ideal for modern full stack applications and is a valuable skill for developers and DevOps engineers.