skip to content
MrMiss Blog
cloud application

DevOps Simplified: Easy-to-Use Container Projects Deployment

/ 6 min read

As a developer, we’re constantly facing challenges in managing and deploying our projects or applications. Navigating the complexities of making application live on the internet can often seem daunting. In this article, we will explore an easy, streamlined flow for our application deployment, making the process not just manageable, but intuitive and efficient.

Here, we assume you’re already familiar with Docker basics and will focus on tools like Docker Compose, Portainer, and Nginx. Ready to streamline our projects from development to live environments? Let’s dive in.

The Main Tools

There are several tools to help managing our projects.


Portainer is a lightweight management tool that simplifies the way we manage our Docker containers, Docker Swarm, and Kubernetes environments. It provides a user-friendly web interface that allows us to easily manage our Docker host or Swarm cluster. We’re using Portainer here to manage our containers stacks.

Nginx Proxy Manager

Nginx Proxy Manager is a tools that provide an easy way to manage an Nginx instance as a proxy host to our Application from the live internet. This tools also provide a free SSL using Let’s Encrypt if we do not own a custom SSL. This Nginx Proxy Manager will act as our application gateway to the public internet.

Step By Step

1. Setup Git Repo

This git repo will be the manifest for all of our application. We can manage all docker compose file of our application here. We can upload it to private GitHub repo to have all automation versioning bump with Renovate Bot

A manifest repo file tree is

├── renovate.json
├── app 1
│   └── docker-compose.yml
└── app 2
    └── docker-compose.yml

with the renovate.json

  "$schema": "",
  "extends": ["config:recomended", "docker:enableMajor", ":dependencyDashboard"],
  "prConcurrentLimit": 0,
  "prHourlyLimit": 0,
  "docker-compose": {
    "fileMatch": ["(^|/)(?:docker-)?compose[^/]*\\.ya?ml$"]

Follow Renovate docs for advanced configs e.g: private image registry (gcr, ghcr, etc)

With this manifest repository, we will receive a PR from renovate every update on the docker image tag.

2. Create a Docker Networks

Docker network is a feature of docker to define and manage network to connect a container. We have to connect all of our container in one network so that it will be able to communicate to each other.

docker network create my_network

3. Spin Up Portainer Instance

To spin up a Portainer instance is simple, all we need is a single docker-compose.yml file.

version: "3.9"

    image: portainer/portainer-ce:alpine
    container_name: portainer
    privileged: true
      - ./portainer_data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped
      - 9443:9443
    name: my_network
    external: true

Now that the installation is complete, we can log into Portainer dashboard on:


We might have to replace the localhost with the server IP’s.

4. Boots up Nginx Proxy Manager

Now we need to run the Nginx Proxy Manager for our reverse proxy. all we need also a single docker-compose.yml file.

version: "3.9"

    image: jc21/nginx-proxy-manager:latest
    container_name: nginx_proxy
    restart: unless-stopped
      - 80:80
      - 81:81
      - 443:443
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    name: my_network
    external: true

Follow the docs for more advanced instruction

There are 3 core ports exposed here. 80 and 443 is the gateway for public HTTP and HTTPS ports. 81 is used for the Nginx Proxy Manager dashboard.

We’ll need to log into the nginx dashboard via


Again, we might have to replace the localhost with the server IP’s. It generate a default user [email protected] and password changeme. You will be asked to modify this at first.

5. DNS Setup

After running Portainer and Nginx Proxy Manager, we need to setup our DNS to point to our server. I’m using cloudflare here for the example.

DNS Records

Setup both desired Nginx and Portainer domain/subdomain.

6. Nginx & Portainer domain setup

On the nginx proxy earlier, go to Proxy Host and add a new one

Nginx Host
Nginx Proxy Manager Host

Insert our domain name we setup earlier on Cloudflare and forward it to localhost with the port 81 as the dashboard we currently in are on port 81. Here we can also tell Nginx Proxy Manager to generate a new SSL.

Portainer Host

Alt text

Point this for the container name running Portainer. If you follow my compose file the name is portainer, so the hostname will be portainer. This is why we need to create a global docker network as Nginx needs to communicate with the app container.

After saving both of the host, we can access our dashboard by the domain we configure. and from now on, no longer need to use the Server IP’s to go to the dashboard.

7. Deploying Application

To deploy our application, we need to add it to our manifest repository.

├── renovate.json
├── nginx
│   └── docker-compose.yml
├── portainer
│   └── docker-compose.yml
└── your application
    └── docker-compose.yml

Don’t forget to add the Network config on our compose file of the application!

    name: my_network
    external: true

Then we need to add that application on the Portainer dahsboard. Go to environment -> stacks -> add stacks

Fill the name of our stack then choose ‘Repository’ for the build method. Enter all of the information needed and don’t forget to turn on the GitOps updates. With this setting, Portainer will send a polling to check updates of the compose file. We don’t need to setup manually again, Portainer will automate all the deploying process.

Portainer GitOps update

Deploy the stack when all done.

8. Seting up Domain for the application

After our container boots up on Portainer, all we need is assigning a domain for our application. Refer to step 5 first to add it on the DNS records. Then we need to configure the proxy host on Nginx Proxy Manager.

app proxy host

That’s all, it was a bit complicated at first but it is worth the time when there is a version update. All we need is merge the version bump from Renovate Bot and Portainer will spin it up automatically.

Notable Notes

Exposed Ports

With the docker network we setup earlier, we no longer need to expose a port on each container. All communication cross-container are handled by the docker Network as long as the container are connected to the same docker network.

We can safely remove the port config on Portainer and Nginx Proxy Manager except the 80:80 and 443:443 as it now become the gateway for all application we have to the public internet.

final compose file

Wrap Up

In this article, we have learned how to streamline an application delivery using docker compose, Portainer and Nginx Proxy Manager.

It’s not a one-size-fits-all solution but rather a practice that can be adapted to our specific needs. Not as powerful as Kubernetes, but this software delivery pipeline are more than enough for small to middle scale application.

That’s it for this article. Thank you for reading!