I set up Docker containers for the frontend and backend. I used Docker Compose to manage both containers.

Reasons for Using Docker

I mainly chose to use Docker since it’s a common tool in industry. I also wanted to improve my development process.

  • Efficiency: Instead of using multiple commands to set up dependencies and build/run the app, I could use one Docker Compose command.
  • Portability: The app will run the same across different machines or environments.
  • Clean development: When you start containers fresh, it will avoid issues from previous files or configurations.
  • Scalability: I haven’t done this yet, but using Docker containers can make it easier to run multiple instances of the backend and/or frontend to handle more requests.

Backend Container

Copy project files, install dependencies, and start the frontend

# python:3.9-slim includes the Python interpreter but doesn't have unecessary packages
FROM python:3.9-slim 
 
# Install git and other system dependencies
	# apt-get update: refresh local package index, which is a 
		# list of available software. ensures that you are using 
		# the latest version.
	# && run the next command if the previous one worked
	# apt-get install -y git: install git package and 
		# automatically answer yes to any prompts
	# rm -rf /var/lib/apt/lists/*: delete temp cache files from 
		# apt-get update. remove unecessary files to save space 
		# for Docker image.
RUN apt-get update && apt-get install -y \
    git \
    && rm -rf /var/lib/apt/lists/*
 
# make a directory called "app" in the container. the next 
	# commands will happen inside this folder.
WORKDIR /app
 
# copy the requirements.txt file into the container. "." means
	# that it will go into the current folder, which in this case 
	# is app.
COPY requirements.txt .
 
# install the python libraries in the requirements.txt
	# --no-cache-dir: stops pip from saving the cache (installer 
	# files) inside the image
RUN pip install --no-cache-dir -r requirements.txt
 
# copy 
	# first ".": copy files from the current local directory
	# second ".": the copied files go into the current container
		#directory
COPY . .
 
# create an environment variable inside the container
	# don't output warning messagse from git python
		# makes the logs easier to read
		# these warnings are usually harmless, so they don't 
			# affect important functionality features
	# GitPython is a library that lets the program do Git 
		# actions such as cloning and committing
	# the Git index (staging area) is a data structure that 
		# tracks changes
	# refreshing the Git index might fail or not be needed inside 
		# a Docker container. since the app is only reading a git
		# repository, you don't need to update the staging area
		# anyways.
ENV GIT_PYTHON_REFRESH=quiet
 
# automatically run this command when the container starts
	# uvicorn: start uvicorn web server
	# main:app: look for the object called app inside main.py
	# --host 0.0.0.0: listen on all network interfaces 
		# so that the container can send/receive data from anyone.
		# allows the server to be accessible outside the container
	# --port 8000: run appliaction on this port, which is common
		# for web APIs
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

ssh -i Frontend Container

Copy project files, install dependencies, set up .env for API keys, and start the frontend

# node:20-alpine is small and resource efficient; it includes Node.js and Alpine Linux
FROM node:20-alpine
 
WORKDIR /app
 
COPY package*.json ./
RUN npm install
 
COPY . .
 
EXPOSE 3000
CMD ["npm", "run", "dev"]
 

Docker Compose

Choose the ports

Build the frontend and backend containers

services:
  backend:
    build: ./backend
    ports:
      - "8000:8000"
    env_file:
      - ./backend/.env
    volumes:
      - ./backend:/app
      - /app/venv  # Exclude Python virtual env if created
 
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
      - /app/node_modules  # Exclude node_modules from volume mount
      - /app/.next         # Exclude Next.js build cache