Skip to content

Nginx Proxy Manager (NPM) is a docker contained app that provides a easy yet reliable and secure way to achieve reverse proxying using Nginx.

It provides a user-friendly UI, automated SSL certs renewal, easy setup, user management and advanced configuration over Nginx reverse proxy.

Here, we use NPM to easily interface all our apps without exposing there port on the host directly and by taking benefits off docker bridge network driver.

Before starting make sure you already created the homelab bridge network by running :

Terminal window
docker network create -d bridge homelab

Now create a docker-compose.yml file and paste this :

version: "3.7"
services:
app:
container_name: npm_app
image: jc21/nginx-proxy-manager:2.9.19
restart: unless-stopped
ports:
- "80:80"
- "81:81"
- "443:443"
- "25500-25600:25500-25600"
- "5440:5440"
env_file:
- stack.env
healthcheck:
test: ["CMD", "/bin/check-health"]
interval: 10s
timeout: 3s
depends_on:
- db
volumes:
- /home/raphaelgc/apps/nginx-pm/data:/data
- /home/raphaelgc/apps/nginx-pm/letsencrypt:/etc/letsencrypt
networks:
- homelab
db:
container_name: npm_db
hostname: npm_db
image: jc21/mariadb-aria:latest
restart: unless-stopped
env_file:
- stack.env
volumes:
- /home/raphaelgc/apps/nginx-pm/data/mysql:/var/lib/mysql
networks:
- homelab
networks:
homelab:
external: true

2 containers are now running : npm_app (NPM itself) and npm_db (the mariadb database).

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>404 Not found</title>
<style>
*,
:before,
:after {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb;
margin: 0;
}
:root {
--bg: #f9fafb;
--text-primary: #333;
--text-secondary: #64748b;
--button-text: #f8fafc;
--button-bg: #2563eb;
--button-accent: #3b82f6;
}
html {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', Segoe UI Symbol,
'Noto Color Emoji';
}
body {
margin: 0;
line-height: inherit;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100vh;
overflow: hidden;
color: var(--text-primary);
background-color: var(--bg);
}
h1 {
position: relative;
padding-bottom: 0.75rem;
margin-bottom: 0.75rem;
}
p {
color: var(--text-secondary);
}
h1:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 70%;
height: 1px;
background-color: var(--text-secondary);
}
a {
padding: 0.5rem 0.75rem;
margin-top: 1rem;
background-color: var(--button-bg);
border-radius: 10px;
color: var(--button-text);
text-decoration: none;
transition: all 200ms linear;
}
a:hover {
background-color: var(--button-accent);
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1),
0 4px 6px -4px rgb(0 0 0 / 0.1);
}
a:focus {
outline: var(--button-accent) 2px solid;
outline-offset: 2px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--button-bg: #1e40af;
--button-accent: #1d4ed8;
}
}
</style>
</head>
<body>
<h1>404 Not found</h1>
<p>Please ask Raphaël for details on error.</p>
<p>Merci de demander à Raphaël pour plus d'informations.</p>
<a href="https://dashboard.lab.raphael-catarino.fr/">Go home</a>
</body>
</html>
error_page 502 /error-bad-gateway.html;
proxy_intercept_errors on;
location /error-bad-gateway.html {
internal;
root /data/nginx/error_pages;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>502 Bad gateway</title>
<style>
*,
:before,
:after {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb;
margin: 0;
}
:root {
--bg: #f9fafb;
--text-primary: #333;
--text-secondary: #64748b;
--button-text: #f8fafc;
--button-bg: #2563eb;
--button-accent: #3b82f6;
}
html {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', Segoe UI Symbol,
'Noto Color Emoji';
}
body {
margin: 0;
line-height: inherit;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100vh;
text-align: center;
overflow: hidden;
color: var(--text-primary);
background-color: var(--bg);
}
h1 {
position: relative;
padding-bottom: 0.75rem;
margin-bottom: 0.75rem;
}
p {
color: var(--text-secondary);
}
h1:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: auto;
width: 70%;
height: 1px;
background-color: var(--text-secondary);
}
a {
padding: 0.5rem 0.75rem;
margin-top: 1rem;
background-color: var(--button-bg);
border-radius: 10px;
color: var(--button-text);
text-decoration: none;
transition: all 200ms linear;
}
a:hover {
background-color: var(--button-accent);
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1),
0 4px 6px -4px rgb(0 0 0 / 0.1);
}
a:focus {
outline: var(--button-accent) 2px solid;
outline-offset: 2px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--button-bg: #1e40af;
--button-accent: #1d4ed8;
}
}
</style>
</head>
<body>
<h1>502 Bad gateway</h1>
<p>Please ask Raphaël for details on error.</p>
<p>Merci de demander à Raphaël pour plus d'informations.</p>
<a href="https://dashboard.lab.raphael-catarino.fr/">Go home</a>
</body>
</html>

To create a complete backup, be sure to backup data and letsencrypt folders, then run the following command to backup the sql db :

Terminal window
docker exec -it npm_db mysqldump --user=npm --password=npm db1 -h 127.0.0.1 > ~/apps/nginx-pm/backups/npm-export-$(date +'%d-%m-%Y_%H:%M:%S').sql

To recover from a backup, copy those two folders, and the .sql backup file, then run :

Terminal window
docker exec -i npm_db mysql --user=npm --password=npm db1 -h 127.0.0.1 < npm-export.sql

Replaced by App iconTraefik for simplicity and security reasons.