Containerize a Tetris App with Docker

Package a Tetris game into a Docker container and publish it to Docker Hub.

Introduction

30 Second Summary

When you build something cool on your computer, sharing it with someone else is surprisingly hard. They need the right software, the right settings, and often a bit of luck for it to work on their machine.

In this project, you will package a playable Tetris game into a Docker container and publish it so anyone in the world can run your game with a single command.

What You'll Build

You'll type one command in your terminal and watch a fully playable Tetris game appear in your browser, served from inside a container that could run identically on any machine in the world.

By the end of this project, you'll have:

  • A running Docker container serving a Tetris game you can play in your browser, built from a Dockerfile you wrote from scratch.
  • A published Docker Hub image that anyone can pull and run on their machine without installing anything beyond Docker.
  • Hands-on experience with the build-tag-push workflow used in real CI/CD pipelines to ship software from a developer's laptop to production.
  • Secret Mission: Add a custom nginx configuration with gzip compression and caching headers for production-grade performance.

Are there any prerequisites?

No prior Docker or DevOps experience is needed. This guide walks you through installing every tool from scratch. You just need a Mac and a free Docker Hub account.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters.

You're going to take a playable Tetris game, package it inside a Docker container with an nginx web server, and push it to Docker Hub so anyone in the world can run it with a single command.

Install Docker Desktop

Docker is the containerization platform you'll use throughout this project. Before you can build or run any container, you need the Docker engine running on your Mac.

Docker Desktop bundles the Docker engine, the Docker CLI, and a visual dashboard into a single install. In this step, you'll get it running and confirm everything works.

In this step, get ready to:
  • Install Docker Desktop on your Mac.
  • Verify the Docker engine is running.
  • Confirm Git is available for the next step.
Download and install Docker Desktop

Now open Docker Desktop for the first time.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Docker and press Enter to open Docker Desktop.
  • The first launch takes a minute while macOS runs security checks. Wait for the Docker Desktop window to appear.
  • Accept the subscription agreement when prompted.
  • Choose Use recommended settings and click Finish.

Is Docker Desktop free?

Docker Desktop is free for personal use, education, and small businesses with fewer than 250 employees and less than $10 million in annual revenue. This project qualifies for the free tier.

Wait for the Docker engine to fully start. You'll see a green status indicator in the bottom-left corner of the Docker Desktop window when it's ready.

Verify Docker is running

With Docker Desktop running, you can now test the Docker CLI in Terminal.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal and press Enter to open it.
  • Check that the Docker CLI is installed by running this command:
docker --version

✔️ I see a version number

You should see something like Docker version 27.x.x printed. Docker is installed and ready.

ⓧ Command not found

If Terminal says the docker command is not found, Docker Desktop has not finished starting. Try these fixes:

  • Check that Docker Desktop is running (look for the whale icon in your menu bar at the top of the screen).
  • If it's not running, open Docker Desktop again using Spotlight Search.
  • Wait 30 seconds for the engine to fully start, then try docker --version again.
  • If it still fails, close Terminal completely and reopen it so it picks up the new PATH.

Now for the real test. Before you run the next command, what do you think will happen when Docker tries to run a container called hello-world for the first time on a machine that has never pulled an image before?

  • Test the Docker engine by running this command:
docker run hello-world

Docker downloads a tiny test image from Docker Hub, creates a container from it, and runs it. You'll see download progress lines, then a message starting with "Hello from Docker!" confirming your installation is working correctly.

What does this command do?

The docker run command creates and starts a container from an image. Since hello-world does not exist locally yet, Docker automatically pulls it from Docker Hub (the public image registry) before running it.

This is the same pull-then-run pattern you'll use later to share your own images with others.

Seeing an error about the Docker daemon?

If you see a message about not being able to connect to the Docker daemon, it means the Docker engine has not finished starting. Make sure Docker Desktop is open and shows a green status indicator, then try the command again.

help me troubleshoot why docker run hello-world is failing on my Mac

Confirm Git is available

You'll need Git in the next step to clone the Tetris app source code. macOS often has Git pre-installed as part of the Command Line Tools.

  • Check if Git is installed by running this command:
git --version

✔️ I see a version number

You should see something like git version 2.x.x printed. Git is ready to go.

ⓧ I'm prompted to install Command Line Tools

If macOS shows a dialog asking you to install Command Line Developer Tools, that means Git is not yet installed. Here's what to do:

  • Click Install in the dialog that appeared.
  • Wait for the download and installation to complete. This can take a few minutes.
  • Once installation finishes, run git --version again to confirm it now shows a version number.

Still not seeing a Git version?

If the Command Line Tools installation failed or git --version still does not work after installation, try running xcode-select --install in Terminal. This triggers the installer manually.

help me get Git working on my Mac

Docker is running, Git is ready, and your Terminal is open. Next up, you'll clone a Tetris game and see it running before you containerize it.

Clone the Tetris App and Run It Locally

Docker is installed and ready to go. Now you need an application to containerize.

Before you package anything into a container, you should see it running on its own first. This gives you a baseline to compare against once it runs inside Docker.

In this step, get ready to:
  • Clone the Tetris source code from GitHub.
  • Open the game in your browser and confirm it works.
  • Understand why this local approach has limitations that containers solve.
Clone the Tetris source code

You'll use a simple HTML5 Canvas and JavaScript Tetris game hosted on GitHub. It requires no build tools, making it a perfect first candidate for containerization.

  • Create your project folder and clone the game into it by running these commands in your Terminal:
mkdir ~/devsecops-tetris
cd ~/devsecops-tetris
git clone https://github.com/bsord/tetris.git app
cd app

What do these commands do?

  • mkdir ~/devsecops-tetris creates a project folder in your home directory.
  • git clone downloads the Tetris repository from GitHub into a subfolder called app.
  • cd app moves you into the folder containing the game files.
  • Confirm the files are there by running:
ls

You should see index.html along with JavaScript and other game asset files listed in the output.

Seeing 'fatal: repository not found'?

  • Check your internet connection. The git clone command downloads files from GitHub, so it needs an active connection.
  • Make sure you typed the URL exactly as shown, including .git at the end.
  • Ask for help: Why is my git clone failing for the bsord/tetris repository?
Open the game in your browser

The game files are now on your machine. Before you run this next command, what do you think you'll see when you open a raw index.html file in your browser?

  • Open the Tetris game in your browser by running:
open index.html

A Tetris game board appears in your browser. Use the arrow keys to move pieces left and right, and the Up arrow to rotate them.

Don't see the game?

  • Make sure your current directory is ~/devsecops-tetris/app. Run pwd to check.
  • If you see a blank page, try a hard refresh with Cmd+Shift+R.
  • Ask for help: Why isn't my Tetris game loading when I open index.html?
Understand why containers solve this problem

The game works on your machine right now. But what if you want to share it with someone else?

Why can't you just send the files?

The other person would need a web server installed, the right file paths configured, and the correct browser setup. Every machine is different, so "it works on my machine" becomes a real problem.

A container packages the app AND the web server (nginx) together into one portable unit. Anyone with Docker can run it identically, regardless of their operating system.

You have a working Tetris game on your machine. Next up, you'll write a Dockerfile that packages this game into a portable container image anyone can run.

Write a Dockerfile and Build Your Image

You have a playable Tetris game on your machine. The catch is that it only works because your browser can reach your local file system directly.

A Dockerfile is the recipe that tells Docker exactly how to package your app into a portable image. Without one, Docker has no idea which files to include or what server to use to serve them.

In this step, get ready to:
  • Write a Dockerfile that packages your game with an nginx web server.
  • Build a Docker image from your Dockerfile.
  • Verify the image exists and check its size.
Create the Dockerfile

A Dockerfile is a plain text file with no extension. Each line is an instruction that tells Docker what base to start from, what files to include, and how the container should behave.

  • You should already be in the ~/devsecops-tetris/app directory from the previous step.
  • Create a new file called Dockerfile (no file extension) in this directory using your text editor, and paste the following content:
FROM nginx:alpine
COPY . /usr/share/nginx/html
EXPOSE 80

What does this Dockerfile do?

  • FROM nginx:alpine starts from the official nginx web server image. The alpine variant is tiny and secure.
  • COPY . /usr/share/nginx/html copies all your game files into the directory that nginx serves to browsers.
  • EXPOSE 80 documents that the container will listen on port 80 for web traffic.
  • Save Dockerfile.
  • Confirm the file exists by running:
ls Dockerfile

You should see Dockerfile printed in the output.

Don't see the file?

  • Make sure you saved the file as exactly Dockerfile with a capital D and no extension. Not Dockerfile.txt, not dockerfile.
  • Confirm you saved it inside ~/devsecops-tetris/app, not a parent or sibling folder.
  • Help me find my Dockerfile

✔️ Awesome, I've got everything!

Great. Make sure you have saved Dockerfile before moving on to the build.

ⓧ I'd like to double check the full code

Your Dockerfile should contain exactly this:

FROM nginx:alpine
COPY . /usr/share/nginx/html
EXPOSE 80
Build the Docker image

With the recipe in place, you can now ask Docker to read it and produce an image. An image is the finished, portable package that any machine with Docker can run.

  • Build your image by running this command:
docker build -t tetris-app .

What does this command do?

  • docker build reads the Dockerfile in the current directory and produces an image.
  • -t tetris-app tags (names) the resulting image so you can reference it later.
  • The . at the end tells Docker to use the current directory as the build context (the set of files available to COPY).

Docker pulls the nginx:alpine base image, copies your game files in, and writes the finished image to your local image store. You will see each layer being created in the terminal output.

Seeing a 'Cannot connect to the Docker daemon' error?

  • Docker Desktop must be running for the build to work. Check your menu bar (macOS) for the Docker whale icon.
  • If Docker Desktop is not running, open it from Applications and wait for the status to show "running" before retrying the command.
  • Help me fix my Docker build error
Verify the image was created

Before you run the next command, predict what you expect to see. What image name and tag should appear? What size range would make sense for a small web server plus some JavaScript files?

  • Check your local images by running:
docker images

You should see tetris-app listed with tag latest, a size around 40-50 MB, and a recent creation timestamp.

Why is it so small?

The nginx:alpine base image is only about 40 MB because Alpine Linux strips out everything a web server does not need. Your game files add just a few hundred kilobytes on top. Small images download faster and have a smaller attack surface.

Your Tetris game is now sealed inside a portable Docker image. Next up, you will run it as a live container and discover how Docker networking keeps things isolated.

Run the Container and Hit the Problem

You have a Docker image sitting on your machine. An image is a blueprint, not a running application. To actually serve your Tetris game, you need to create a container from that image.

In this step, you will start a container and immediately discover a networking gap that every Docker beginner hits. Fixing it teaches you one of the most important Docker concepts: port mapping.

In this step, get ready to:
  • Run a container and discover Docker's network isolation.
  • Fix the problem with port mapping and access the game in your browser.
  • Inspect the running container from the inside.
Run the container without port mapping

Your tetris-app image is ready. Time to turn it into a running container.

  • Start a container from your image by running this command:
docker run -d --name tetris-no-port tetris-app

What does this command do?

The -d flag runs the container in detached mode (in the background). The --name tetris-no-port gives it a human-readable name so you can reference it later.

You will see a long string of characters printed. That is the container ID, confirming the container started successfully.

The container is now running with nginx serving your Tetris game on port 80 inside the container. What do you think will happen when you try to open it in your browser?

  • Open http://localhost:80 in your browser.

The page does not load. You get a connection error or an empty response.

Why doesn't it load?

The Tetris game is running inside the container on port 80, but Docker isolates container networks from your Mac. The container's port 80 is not automatically connected to your Mac's port 80.

This is Docker's network isolation in action. You need to explicitly tell Docker to map a port on your Mac to a port inside the container. This is called port mapping.

Fix it with port mapping

Now that you have seen the problem, the fix is a single flag. First, remove the broken container.

  • Stop and remove the container that has no port mapping by running:
docker rm -f tetris-no-port
  • Run the container again, this time with port mapping, by running:
docker run -d --name tetris-game -p 8080:80 tetris-app

What does -p 8080:80 do?

The -p 8080:80 flag maps your Mac's port 8080 to the container's port 80. Traffic that hits localhost:8080 on your Mac gets forwarded into the container on port 80, where nginx is listening.

The format is host_port:container_port. You can pick any available port on the left side.

  • Open http://localhost:8080 in your browser.

The Tetris game loads and plays exactly as before. Use the arrow keys to move pieces and Up to rotate. The difference is that this time, the game is served from inside a Docker container.

Game not loading at localhost:8080?

Make sure you typed the port correctly. The URL is http://localhost:8080 (port 8080, not 80 or 8000).

Run docker ps to check if the container is running. If it is not listed, another process may already be using port 8080. Try a different host port like -p 9090:80 instead.

Still stuck? Help me debug why my Docker container isn't serving the game at localhost:8080

Inspect the running container

Your container is running and serving the game. Docker gives you several commands to peek inside a running container and see what is happening.

  • View your running containers and their port mappings by running:
docker ps

You should see tetris-game listed with 0.0.0.0:8080->80/tcp in the PORTS column. This confirms the port mapping is active.

  • Check the nginx access logs by running:
docker logs tetris-game

You should see HTTP request entries from when you loaded the game in your browser. Each page load generates log lines for the HTML, JavaScript, and image files nginx served.

  • Open a shell inside the running container by running:
docker exec -it tetris-game sh

You are now inside the container. It is a tiny Linux filesystem running independently from your Mac.

  • List the game files nginx is serving by running:
ls /usr/share/nginx/html

You should see your Tetris game files (index.html, JavaScript files, and the Dockerfile). These are the files your COPY . /usr/share/nginx/html instruction placed here during the build.

  • Exit the container shell by typing:
exit

What just happened?

You jumped inside a running container and browsed its filesystem. The container has its own isolated environment with only what nginx:alpine provides plus your copied files. Nothing else from your Mac is visible inside.

This isolation is what makes containers portable. The same image runs identically on any machine with Docker installed.

Your Tetris game is running inside a container and you understand the networking that connects it to the outside world. Next up, you will push this image to Docker Hub so anyone can pull and run your game with a single command.

Tag and Push to Docker Hub

Your Tetris game is running inside a container, but the image only exists on your machine. If a teammate or a CI/CD pipeline tries to pull it, there is nothing to find.

Docker Hub is a public registry where you can publish images for anyone to download. In this step, you will tag your image with your Docker Hub username, push it to the registry, and prove it works by pulling it fresh.

In this step, get ready to:
  • Log in to Docker Hub from your Terminal.
  • Tag your image and push it to Docker Hub.
  • Verify the push by pulling the image fresh and running it.
Log in to Docker Hub

Before you can push an image, Docker needs to know who you are. The login command authenticates your Terminal session with your Docker Hub account.

  • Log in to Docker Hub by running this command:
docker login

What happens when you run this?

Docker opens a web-based device code flow. You will see a one-time confirmation code and a URL in your Terminal. Press Enter to open your browser, enter the code, and sign in with your Docker Hub credentials.

Once authenticated, your Terminal prints Login Succeeded.

  • Follow the prompts in your Terminal and browser until you see Login Succeeded.

Login not working?

Make sure Docker Desktop is running. The Docker CLI cannot authenticate if the Docker engine is stopped.

If the browser flow times out, try running docker login --username yourusername instead. This bypasses the device code flow and prompts for your password directly in Terminal.

help me log in to Docker Hub from Terminal

Tag and push the image

Docker Hub organizes images by username. To push your image, you need to re-tag it with your Docker Hub username as a prefix and a version number.

  • Tag your local image with your Docker Hub username by running this command:
docker tag tetris-app [[DOCKERHUB_USERNAME="your Docker Hub username"]]/tetris-app:v1.0

What does this command do?

The docker tag command creates a new name for an existing image. It does not copy the image data. It adds a pointer that includes your username (so Docker Hub knows which account to push to) and a version tag (v1.0) so you can track releases.

  • Confirm the new tag exists by running:
docker images

You should see both tetris-app:latest and your newly tagged image listed. They share the same image ID because the tag is just a label.

  • Push the tagged image to Docker Hub by running this command:
docker push [[DOCKERHUB_USERNAME="your Docker Hub username"]]/tetris-app:v1.0

Watch the output as each layer is uploaded. Once all layers finish pushing, your image is publicly available on Docker Hub.

Push denied or authentication required?

This usually means your tag does not match your Docker Hub username exactly. Double-check that the username portion of your tag matches what you signed in with during docker login.

If you see a repository-does-not-exist error, the username in your tag likely has a typo. Re-run the docker tag command with the correct username.

help me push my Docker image to Docker Hub

Verify the push worked
  • Open your browser and go to https://hub.docker.com.
  • Sign in and click Repositories in the top navigation.
  • You should see tetris-app listed with the v1.0 tag.

Seeing it on the website is good. Pulling it fresh is proof. Before you run the next commands, what do you expect to happen when Docker tries to pull an image you already have locally?

  • Remove the local copy of your pushed image by running:
docker rmi [[DOCKERHUB_USERNAME="your Docker Hub username"]]/tetris-app:v1.0
  • Pull the image fresh from Docker Hub and run it on a new port by running these commands:
docker pull [[DOCKERHUB_USERNAME="your Docker Hub username"]]/tetris-app:v1.0
docker run -d -p 9090:80 [[DOCKERHUB_USERNAME="your Docker Hub username"]]/tetris-app:v1.0

What do these commands do?

The docker rmi command removes the local image so the pull must download fresh from Docker Hub instead of using a cached copy.

The docker pull command downloads the image layers from Docker Hub. The docker run command starts a new container on port 9090 so it does not conflict with your existing container on port 8080.

  • Open http://localhost:9090 in your browser.

The Tetris game loads and plays exactly as before. This copy was pulled entirely from Docker Hub, proving anyone with Docker can run your game with a single command.

Game not loading on port 9090?

Run docker ps to check if the new container is running. If it exited immediately, run docker ps -a to find the container ID, then docker logs followed by the container ID to see the error.

If port 9090 is already in use, try a different port like 9091:80 and open http://localhost:9091 instead.

help me run a container from my Docker Hub image

Your image is now live on Docker Hub for anyone to pull. You have completed the full Docker lifecycle: source code to Dockerfile to image to registry to running container. Next up, you will take this further with a Secret Mission to add production-grade performance to your container.

Secret mission

Add Production-Grade Nginx Configuration

Your Tetris container works, but it serves files with no compression and no caching. Add a custom nginx configuration to enable gzip compression and cache headers. These are the same optimizations used in real production deployments.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally and uses free Docker Hub public repositories, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Docker containers running locally (free, uses your Mac's resources).
  • Docker images stored locally on your Mac.
  • Docker Hub repository with v1.0 and v2.0 tags (free for public repositories).
  • Project folder ~/devsecops-tetris on your Desktop.

Keep everything running

No action needed. Choose this if you are still actively building or plan to use these containers for the next project in this series.

  • Your containers continue running and are accessible at http://localhost:8080.
  • Your Docker images stay cached locally for fast rebuilds.
  • Your Docker Hub repository remains publicly accessible for anyone to pull.
  • Docker Desktop stays installed on your Mac for future Docker projects.

Pause - I'll come back to this later

Stop running containers to free up CPU and memory. Your images and files remain on disk, ready to restart anytime.

  • Stop all containers from this project by running:
docker stop tetris-game
  • Check for any other running containers from this project by running:
docker ps
  • If you see other containers using the tetris-app image, stop them with docker stop <container-id>.
  • Stopped containers consume no CPU or memory. You can restart them later with docker start tetris-game.

Delete - I don't want to use this again

Remove all project resources from your Mac and Docker Hub. This frees disk space and removes the public repository entirely.

  • Stop and remove all containers from this project by running:
docker rm -f tetris-game
  • Check for any unnamed containers from this project by running:
docker ps -a
  • If you see containers using tetris-app or your Docker Hub image, remove them with docker rm -f <container-id>.
  • Remove all local Docker images from this project by running:
docker rmi tetris-app
docker rmi your-dockerhub-username/tetris-app:v1.0
docker rmi your-dockerhub-username/tetris-app:v2.0
  • Delete the project folder from your Mac by running:
rm -rf ~/devsecops-tetris
  • Delete the Docker Hub repository by following these steps:
  • Sign in to Docker Hub at https://hub.docker.com.
  • Select My Hub, then Repositories.
  • Select your tetris-app repository.
  • Select the Settings tab.
  • Select Delete repository.
  • Enter the repository name to confirm, then select Delete Repository Forever.

That's a Wrap!

That's a Wrap!

Nice work! You just containerized a Tetris game, pushed it to a public registry, and made it runnable by anyone in the world with a single command.

You've learned how to:

  • Write a Dockerfile from scratch using nginx:alpine as a base image, packaging a static web app into a portable Docker image.
  • Discover port mapping by running a container without it, hitting the networking wall, and fixing it with the -p flag to connect your Mac's ports to the container's ports.
  • Tag and push your image to Docker Hub, making it globally accessible so anyone can pull and run your game without installing anything else.
  • Secret Mission: Added a production-grade nginx configuration with gzip compression and cache headers to optimize your containerized app for real-world performance.

Ready to quiz yourself?