Nginx Reverse proxy for Docker flow diagram

Host Any App on Docker Nginx and Certbot on AWS LightSail

In this tutorial i’ll show you how to prepare a server with docker environment so you could deploy any application there.

This tutorial will cover following key checkpoints:

  1. Install Docker on Ubuntu 24.04 LTS.
  2. Install Nginx.
  3. Install Certbot.
  4. Deploy a test app on docker with Dockerfile and Docker run command.
  5. Deploy a test app on docker with docker compose.
  6. Configure reverse proxy.
  7. Obtain let’s encrypt certificate with certbot.
  8. Access the App on browser with https using nginx reverse proxy.

Let’s get started First thing first, you need a linux server, with domain pointing to it. I had used an Amazon lightsail server instance with ubuntu 24.04 LTS. I configured my domain demodev.app by pointing A record to the ip address of the server. If you want to check a detailed tutorial about this you can read this article Lightsail domain management.

Once your domain and server is ready and you can access the terminal we can get started. First just run following commands to ensure server is up to date with repositories and packages update.

sudo apt get update
sudo apt get upgrade -y

Install Docker on Ubuntu 24.04 LTS

Installing docker is really easy the official docker documentation provides the commands which can be easily copy pasted.

It’s a two step approach, first set up repository and second install docker.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Once repository added, you can proceed to install docker.

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

This will take care of installing docker and docker compose both.

Once done you can simply type next command to test it.

sudo docker run hello-world

For now you need to use sudo but moving on to next step you will no longer need this. Just confirm that hello word was properly executed and then follow next steps to add the user to docker group, so that you no longer need to run docker as sudo everytime.

#create docker group, ideally it would exist already
sudo groupadd docker

#add user to the group
sudo usermod -aG docker $USER

#activate the changes
newgrp docker

#test again
docker run hello-world

That’s it you are done with docker now.

Let’s go to next step.

Install Nginx

Nginx will help us create create reverse proxy, this is required because everything you put on docker is available on a certain port which is mapped on local machine. We will need to route all requests coming from outside to the local port using a reverse proxy. something like this:

Nginx Reverse proxy for Docker flow diagram

So let’s install and activate Nginx.

#install nginx and enable
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

Install Certbot

sudo apt install certbot python3-certbot-nginx -y

yes, that’s is that’s how easy it is to install certbot. just one command, and you’ll see that just one command can fetch and configure the certificate for you, but for now let’s go to next step.

Deploy a test app on docker with Dockerfile and Docker run

We will be creating a very simple python app and run it on docker for testing. so let’s start by creating directory for the app.

mkdir ~/docker-dummy && cd ~/docker-dummy
nano app.py

Once the editor is open put the code in there, this is just a simple python code which lists the directories and files on the server.

from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer

PORT = 5000

with TCPServer(("", PORT), SimpleHTTPRequestHandler) as httpd:
    print("Serving at port", PORT)
    httpd.serve_forever()

Let’s create Dockerfile now.

nano Dockerfile
FROM python:3

WORKDIR /app
COPY app.py .

CMD ["python", "app.py"]

Build and run the app.

docker build -t dummy-app .
docker run -d --name dummy-app -p 5000:5000 dummy-app

This code is telling docker to create a dockerised container by the name dummy-app and run the image dummy-app in it, and because we are running python on port 5000, we need to expose it to the docker host(ubuntu) on port 5000. To test it locally we can use curl command, you should see the output like this:

:~/docker-dummy$ curl localhost:5000
127.0.0.1 - - [11/Aug/2025 16:05:36] "GET / HTTP/1.1" 200 -
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="app.py">app.py</a></li>
<li><a href="Dockerfile">Dockerfile</a></li>
</ul>
<hr>
</body>
</html>

If you see the output that means you have successfully ran the app in docker. if you list your docker containers using docker ps you should see the running containers.

Deploy a test app on docker with docker compose

You would not want to run the docker command everytime and it’s kind of difficult to remember and pass all parameters on runtime, so to manage it better we have docker compose. Using this you can just create 1 docker compose file and use it to deploy the app easily.

Create the docker compose file:

docker stop dummy-app
docker rm dummy-app
nano docker-compose.yml
version: "3.8"
services: 
  dummyapp:
    build: .
    container_name: dummy-compose
    ports:
      - "5000:5000"
    restart: always

Copy and Paste the above code in your docker-compose.yml file, this is telling docker to create a container named dummy-compose and run image dummyapp on it, and expose the port 5000. This is similar to what we did with docker run but a cleaner and better approach.

docker-compose up -d

Above command will build and deploy the app and you can test it again using curl localhost:5000

Configure reverse proxy

To configure the reverse proxy we have already installed nginx. now we need to create configuration files only. Let’s create them.

sudo nano /etc/nginx/sites-available/demodev.app
server {
    listen 80;
    server_name demodev.app;

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Save, Create a link in sites-enabled directory as well. Then test and enable configuration.

sudo ln -s /etc/nginx/sites-available/demodev.app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

At this stage you should be able to acces the deployed app without any ssl certificate. the UI would look like this:

docker deployment demonstration

but currently it needs the Https, so let’s get it with certbot.

Obtain let’s encrypt certificate with certbot

We installed the certbot already in step 3, now let’s use it.

sudo certbot --nginx -d demodev.app

This will ask you few questions and then configure the certificate for you. After it’s done the final version of the config file will look like this:

server {
    server_name demodev.app;

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/demodev.app/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/demodev.app/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = demodev.app) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name demodev.app;
    return 404; # managed by Certbot


}

Restart the nginx using command:

sudo systemctl restart nginx


Now after all this when you access the Website on actual url https://demodev.app you should be able to see the directory like it showed in previous step, but this time it’ll be secured by https.

Docker app deployment test result

You can watch the full tutorial on the youtube as well: