How to Install and Use Portainer for Docker management with Nginx Proxy Manager

Portainer is an open-source container management solution for Docker, Kubernetes, and Nomad that simplifies starting, creating and running containers in an easy way. It provides a web-based dashboard to manage containers, images, networks, and volumes.

In this tutorial, you will learn to install and configure the Portainer container management solution on a Linux server and use it to create and manage Docker containers to run different apps. You will also learn to put these containers behind Nginx using the Nginx proxy manager.


  • A Linux server running Ubuntu / Debian / Cent OS / Rocky Linux 8 / Alma Linux.
  • A non-root user with sudo privileges.
  • A Fully Qualified Domain Name (FQDN) pointing to the server for Portainer ( and Nginx Proxy Manager (

Step 1 - Configure Firewall

Cent OS/Rocky Linux/Alma Linux

You should have the Firewalld firewall installed. Check the firewall's status.

$ sudo firewall-cmd --state

Open ports 80, 9443 and 443. Portainer uses port 9443 to expose its web UI via HTTPS. Nginx Proxy Manager uses port 81 for its UI.

$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https
$ sudo firewall-cmd --permanent --add-port=9443/tcp
$ sudo firewall-cmd --permanent --add-port=81/tcp

Reload the firewall to enable the changes.

$ sudo firewall-cmd --reload


Ubuntu and Debian systems use ufw (Uncomplicated Firewall) by default.

Check if the firewall is running.

$ sudo ufw status

If it is running, then open ports 80, 9443 and 443.

$ sudo ufw allow 80
$ sudo ufw allow 443
$ sudo ufw allow 9443
$ sudo ufw allow 81

Open the SSH port if the firewall isn't running.

$ sudo ufw allow "OpenSSH"

Enable the firewall if it is not running.

$ sudo ufw enable

If it is running, reload it to apply the changes.

$ sudo ufw reload

Step 2 - Install Docker

Cent OS/Rocky Linux/Alma Linux

Run the following command to install Docker.

$ sudo yum install -y yum-utils
$ sudo yum-config-manager \
    --add-repo \
$ sudo yum install docker-ce docker-ce-cli


$ sudo apt install ca-certificates curl gnupg lsb-release
$ curl -fsSL | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli


$ sudo apt install ca-certificates curl gnupg lsb-release
$ curl -fsSL | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli

Enable and Start the Docker service.

$ sudo systemctl start docker --now

Add your username to the Docker group.

$ sudo usermod -aG docker $USER

Log out of the system and log back in to apply the change.

Step 3 - Install Docker Compose

Download and install Docker compose binary.

$ sudo curl -L "$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Apply executable permission to the binary.

$ sudo chmod +x /usr/local/bin/docker-compose

Step 4 - Install Portainer

Create a directory for Portainer.

$ mkdir ~/portainer

Switch to the directory.

$ cd ~/portainer

Create and open the Docker Compose file for editing.

$ nano docker-compose.yaml

Paste the following code in it.

version: "3.3"
      image: portainer/portainer-ce:latest
      container_name: portainer
      restart: always
      privileged: true
        - ./data:/data:Z
        - /var/run/docker.sock:/var/run/docker.sock:Z
        - 9443:9443

Save the file by pressing Ctrl + X and entering Y when prompted.

Let us go through the Docker compose file.

  • We are pulling the latest version of the Portainer Community Edition from Docker Hub. Portainer Community Edition is free to use, while their Business edition requires a paid license. You can pull the Business Edition, but you will be asked for the license key to use it.
  • We have named our container as portainer for identification and linking purposes.
  • The restart policy is set to always so that the container remains up during boot up.
  • The privileged: true setting is for Portainer to be able to access the Docker socket and run in a privileged context since we are using SELinux. If you are not using SELinux, you can remove this setting. This setting gives Portainer container access to everything on the host system, including access to the hardware. So, enable this setting only when you know what you are doing.
  • The volumes section maps the folder on the host to the folders in the container using Bind mounts. We have exposed the directory ~/portainer/data to the container for storing any relevant data and Docker socket API for container management. The :Z label tells Docker that we are running SELinux on our host. If you don't have SELinux enabled, you should remove the label.

Start Portainer.

$ docker-compose up -d

Check the status of the container.

$ docker ps
CONTAINER ID   IMAGE                           COMMAND        CREATED         STATUS         PORTS                                                           NAMES
916411e8d12e   portainer/portainer-ce:latest   "/portainer"   5 seconds ago   Up 4 seconds   8000/tcp, 9000/tcp,>9443/tcp, :::9443->9443/tcp   portainer

Step 5 - Access and Configure Portainer

Open the URL https://<yourserverIP>:9443 in your browser, and you will get the following screen.

Portainer Installation Screen

You will be asked to create a new administrator user. Add your user details. Uncheck the box Allow collection of anonymous statistics if you care about privacy. Click the Create user button to start the installation and create a new administrator account.

Next, you will be taken to the following dashboard screen.

Portainer Dashboard

After a few seconds, it will automatically refresh and show you the following screen.

Portainer Home

It will show you the local environment in which Portainer is running. Click on the local environment to get started.

Portainer Environment Homepage

Most of the sections are self-explanatory. The Stacks section helps in creating containers using Docker compose files. You can deploy containers directly using the Containers category in the sidebar. You can configure the current docker environment through the Hosts section. The App Templates section comes with pre-installed Docker compose files for installing the most common applications. You can also create custom templates.

The Settings section allows you to configure various settings such as adding custom Docker registries, adding multiple hosts for Docker swarm, configuring user access, backing up data, and customizing Portainer.

Step 5 - Put Portainer behind a reverse proxy using Nginx Proxy Manager (NPM)

Before moving ahead, let us put Portainer behind a reverse proxy using Nginx Proxy Manager. Nginx Proxy Manager is a Docker application that provides a web management UI for setting up Nginx as a reverse proxy host. It can also be used as a redirect or a streaming host.

Install NPM

The first step is to create a network for Nginx Proxy Manager (NPM). Open the Networks section and click the button Add Network to create a new network.

Portainer Networks List

Give a name to the network and leave all the settings unchanged. Click the Create the network button to finish.

Portainer Create Network Page

Visit the Stacks and create a new stack using the Add stack button.

Portainer Stacks Page

Name the stack as nginx-proxy-manager and paste the following code into it.

version: "3.3"
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm-app
    restart: unless-stopped
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP
      DB_MYSQL_HOST: "npm-db"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm"
      DB_MYSQL_NAME: "npm"
      # Uncomment the line below if IPv6 is not enabled on your host
      # DISABLE_IPV6: 'true'
      - ./npm-data:/data:Z
      - ./letsencrypt:/etc/letsencrypt:Z
      - npm-db
      - npm-network
      - npm-internal

    image: 'mariadb:latest'
    container_name: npm-db
    restart: unless-stopped
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'npm'
      - ./npm-data/mysql:/var/lib/mysql:Z
      - npm-internal

    external: true

Portainer Add Stack Page

We have set two environment variables to set database and root MySQL passwords. Portainer can be used to set secrets using environment variables. Scroll down the page and click the Add environment variable button to add strong passwords.

Portainer Stack Environment Variables

Click the Deploy the stack button to create and start the NPM container.

Access NPM

Open the URL https://<yourserverIP>:81 in your browser, and you will get the following screen. Enter the following default credentials to sign in.

Email address: [email protected] Password: changeme

Next, you will be immediately asked to set a name and an email address. Click the Save button, and you will be asked to create a new password. Click the Save button again to get started.

Nginx Proxy Manager Dashboard

Visit the Hosts >> Proxy Hosts and click the Add Proxy Host button.

Add Portainer as Proxy Host

Enter the domain name as Choose the scheme as https. Enter the name of the container as the Forward Hostname and 9443 as the Forward port. Check the options Block Common Exploits and Websockets Support options.

Portainer NPM SSL options

Switch to the SSL tab and select Request a new SSL Certificate from the dropdown menu. Check the Force SSL and HTTP/2 Support options to secure and optimize your SSL connection. Enter the email address to receive renewal notifications and agree to the terms of service. Click the Save button to finish setting up the Proxy Host for Portainer.

Connect Portainer to the NPM Container

We have set up the Proxy host, but the container is still not connected to the NPM network. Go back to the Portainer dashboard, visit the Containers section, and select the portainer container.

Select npm-network from the dropdown menu under the Connected networks section and click the Join network button to add the Portainer container to the proxy manager's network.

Portainer Connected Networks

You might get an error but refresh the page, and you should see the container added to the NPM network.

Portainer Connected to NPM

You should be able to access Portainer using the URL in your browser.

You can follow a similar procedure to put NPM behind a publicly accessible URL like, as discussed in our Nginx Proxy Manager tutorial.

Now that you have set a public URL for Portainer, you can remove the exposed 9443 port. To do that, go back to the Terminal and switch to the portainer directory.

$ cd ~/portainer

Open the Docker compose file for editing.

$ nano docker-compose.yaml

Remove the ports section by commenting it out, as shown below.

version: "3.3"
      image: portainer/portainer-ce:latest
      container_name: portainer
      restart: always
      privileged: true
        - ./data:/data:Z
        - /var/run/docker.sock:/var/run/docker.sock:Z
      #  - 9443:9443
        - npm-network

    external: true

Save the file by pressing Ctrl + X and entering Y when prompted.

Here, we have added the details of the NPM network because we will need to restart the Portainer container.

Stop the Portainer container.

$ docker-compose down --remove-orphans

Start the container again with the updated configuration.

$ docker-compose up -d

Step 6 - Deploy a container using App Template

Portainer provides several pre-defined templates to launch applications directly with minimal configuration.

Portainer App Templates

Visit the App Templates section and select any template. Give it a name, and select the network for use. Use the advanced options section to deploy custom ports, networks, and volume mounts.

Click the Deploy the container button to finish deploying your application. Here we are deploying the Redis container.

Portainer Redis App Deployment

Step 7 - Manage Containers

Let us manage an existing container. Open the containers page, and you will see all the running containers.

Portainer Containers List

Click the recently created hw-redis container to proceed.

Portainer Container Actions

On top, you can see a list of actions you can perform on a running container. You can stop and kill the container. Recreate will create the container from scratch. The Duplicate/Edit option will allow you to create another identical container allowing you the ability to change settings before launching it.

The Container status shows the running time, IP address and other details about the container.

The Logs option shows the output of the docker logs command. Since the command's output is not cached, every time you refresh the page, the command is run from scratch.

Portainer Container Logs

The Inspect option runs the command docker inspect on the container and shows its output.

Portainer Container Inspect

The Stats option shows you the usage of the container in real-time.

Portainer Container Stats

You can launch the Container console by using the Console option. You will be asked for the command and the system user to run.

Portainer Container Console Configuration

Press the Connect button to launch the console.

Portainer Container Console Shell

The Attach option runs the docker attach command.

There are other options on the Container details page. You can create an image using an existing container. Other options include changing a container's restart policy and connecting or disconnecting a network to an existing container.

Attach an Outside container to Portainer

Creating any container outside of Portainer will show up inside it as long as it is created on the same system Portainer is running. This is possible because Portainer is hooked up with Docker using the websocket.

Let us test by running the Hello World Docker container.

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:10d7d58d5ebd2a652f4d93fdd86da8f265f5318c6a73cc5b6a9798ff6d2b2e67
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:

For more examples and ideas, visit:

Check the container list in the terminal. We are using the -a flag to show the list of all containers, including the stopped ones. You can see the name of the container as sad_williamson.

$ docker ps -a
CONTAINER ID   IMAGE                             COMMAND                  CREATED         STATUS                     PORTS                                                                                  NAMES
5fa46b85d594   hello-world                       "/hello"                 3 minutes ago   Exited (0) 3 minutes ago                                                                                          sad_williamson

Now, check the Portainer Containers page, and the hello world container will show up as stopped in the list with the same name.

Portainer Hello World Container


This concludes our tutorial on installing and using Portainer for Docker management and Nginx Proxy Manager. We will explore building docker images, creating custom containers and using Portainer with Docker swarm in an upcoming tutorial. If you have any questions, post them in the comments below.

Share this page:

7 Comment(s)

Add comment

Please register in our forum first to comment.


By: Paul Jones

Do people really install server package from outside trusted repos?  This is a serious question.

When I see a curl download command for software in a git repo, which seems random, I cringe a bit.  My employer would never, ever, allow that.  We can only get software from vetted sources, usually sources where we have a paid support agreement.

By: jaya

Set the scheme to https  instead of http in the proxy host. the site will be secured by a lock. Thank you for sharing.

By: wd40


Could you please tell me why is there a dedicated network for NPM ? I don't quite get this part.


By: Shane


Why create the network? 

Amazing article, it saved me a lot of time when I was re-installing my portainer server.

If you are new to self-hosted, this article covers everything you need to get started.

Don't listen to Paul Jones below, he's a wagiecuck moron, don't listen to his lies.

By: Bilbazafa

Great article - When I press Save on the SSL part of NGINX, all I get is 'Internal Error' . Do you know why this is?

By: Imran

you told, "We will explore building docker images, creating custom containers and using Portainer with Docker swarm in an upcoming tutorial." Give me those tutorial link..