This docker container makes it a lot easier to manage Nginx configs.
Instead of creating an entire new file with a lot of boilerplate you can just easily add a new line to the config. Nginx Config Manager will create the Nginx config for you, and automatically requests certificates for it using certbot. It will also create Diffie-Hellman parameters, this could take a while when the container launches for the first time. If you use cloudflare, it can also automatically restore your visitor ip addresses. (See Cloudflare)
This version uses nginx:stable-alpine
as a parent container. This allows it to reload nginx by itself.
If you would like to have a standalone version see #standalone
The only required change is CERTBOT_EMAIL
.
Click Here for a list of Environment Options.
version: "3.3"
services:
nginx:
image: netfloex/nginx:v2.4.0
container_name: nginx
environment:
CERTBOT_EMAIL: EMAIL # Required
ports:
- 80:80
- 443:443
volumes:
# Optional
# - ./logs:/var/log/nginx
# - ./nginx_config_files:/etc/nginx/conf.d
- ./data:/app/data # Needed when using custom files or cloudflare, this is used as a cache.
# Required
- ./letsencrypt:/etc/letsencrypt
- ./config:/app/config
You can create a config file using json5, js or yaml.
The file should be placed in the config folder as config.(yml|yaml|json|jsonc|json5|js)
Pick your favorite language to write your config in and create a file like "config/config.yml"
Simple Example:
module.exports = {
cloudflare: true, // When using Cloudflare
servers: {
"example.com": {
proxy_pass: "http://mysite:3000",
subdomains: {
www: "http://mysite:3000" // Converts to www.example.com
}
}
}
};
The above example could be shorter:
module.exports = {
cloudflare: true, // When using Cloudflare
servers: {
"example.com": "http://mysite:3000",
"www.example.com": "http://mysite:3000"
}
};
This example fetches the latest ips from Cloudflare, enables SSL with Certbot, creates DH-Params, and creates two config files:
example.com > http://mysite:3000
www.example.com > http://mysite:3000
A more complete example can be found here.
The entire config is located in one file.
All properties are optional.
The config is validated by zod, so you know when you make mistakes.
If you only need a proxy pass you can shorten it:
Instead of writing:
/* servers: */ {
"example.com": {
proxy_pass: "http://base_domain:80"
}
}
You can also write:
/* servers: */ {
"example.com": "http://base_domain:80"
}
The config can be reloaded by sending a SIGHUP signal to the container. This updates Nginx's configuration files and renews certificates if needed.
When running inside docker:
docker kill --signal=HUP nginx_config_manager
You can use environment variables in your config by using %env:VARIABLE%
, where VARIABLE
is your variable.
Note: currently this is only possible when using JSON
Tip: If your using js, you can also useprocess.env
# environment variables
USERNAME=John
PASSWORD=Doe
// config.json
/* Server/Subdomain/Location: */ {
// Single user
"auth": {
"username": "%env:USERNAME%", // "John"
"password": "%env:PASSWORD%" // "Doe"
}
}
This is a list of options possible inside a (sub)domain or location.
Proxies the request to another location.
When the container starts a DNS Lookup is performed to test if the hostname is valid. To disable this see Environment Options
/* Server/Subdomain/Location: */ {
proxy_pass: "http://hostname:80";
}
This adds a custom CSS file to an application. It should be a url to a CSS file.
This file is downloaded and compressed, its then stored inside /app/custom/css
Configurable.
The compressed CSS is then appended to the end of the <head>
by using Nginx's sub_filter
.
/* Server/Subdomain/Location: */ {
custom_css: "http://example.com/style.css"; // The use of an array is possible too
}
It will add the following directives:
# Don't sent this header to the proxy
proxy_set_header Accept-Encoding "";
sub_filter '</head>' '<link rel="stylesheet" type="text/css" href="/custom_assets/css/**.css"></head>';
sub_filter_once on;
Source theme-park.dev
This allows to use a custom JS file. It should be a url to a JS file.
This file is downloaded, its then stored inside /app/custom/js
Configurable.
This file is appended to the end of the <body>
by using Nginx's sub_filter
.
/* Server/Subdomain/Location: */ {
custom_js: "http://example.com/script.js"; // The use of an array is possible too
}
It will add the following directives:
# Don't sent this header to the proxy
proxy_set_header Accept-Encoding "";
sub_filter '</body>' '<script src="/custom_assets/js/**.js"></script></body>';
sub_filter_once on;
Source theme-park.dev
Option to enable Websocket support, this adds the following to the config:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_http_version 1.1;
Example:
/* Server/Subdomain/Location: */ {
websocket: true; // Default: false
}
Allows adding headers to the response.
Expects an object with key values of the headers.
These headers are sent to the client
/* Server/Subdomain/Location: */ {
headers: {
"Content-Type": "text/html",
"x-powered-by": "overridden"
};
}
Adds a Access-Control-Allow-Origin
header with the supplied value.
Expects an url, or true
/*
to allow any origin.
This may be useful to show a custom message with a custom status code.
This is equivalent to Nginx's return
/* Server/Subdomain/Location: */ {
"return": `404 "Not found"`,
headers: {
"Content-Type": "text/html",
"x-powered-by": "overridden"
};
}
For displaying simple HTML.
This is equivalent to "return":
200 "Message"``
/* Server/Subdomain/Location: */ {
"html": "<h1>Hello World</h1>"
}
Redirect to another location
/* Server/Subdomain/Location: */ {
redirect: "/new_location";
}
Allows for complex redirects, the redirect only happens if the first argument matches.
This is equivalent to Nginx's rewrite
Note: If possible redirect/return should be used.
/* Server/Subdomain/Location: */ {
rewrite: "^/users/(.*)$ /users?id=$1"; // Redirects /users/test to /users?id=test
}
Allows hosting static files, same as root
When not using an absolute path it's relative to /app
/* Server/Subdomain/Location: */ {
static: "site"; // Resolves to /app/site
}
Docker Compose
volumes:
- ./site:/app/site
Enables authorization for a page
Expects an object with username and password. Can also be an array for multiple users.
The password is hashed using Apache's apr1 md5 algorithm. A .htpasswd file is created and stored inside $DATA/auth
Tip: If you don't want your password visible inside the config use Variable Substitution
/* Server/Subdomain/Location: */ {
// Single user
auth: {
username: "user",
password: "pass"
},
// Shorthand for using default username (admin by default)
auth: "password",
// Multiple Users
auth: [
{
username: "bob",
password: "hi"
},
{
username: "other",
password: "user"
}
]
};
If you do not specify the username the default will be used (admin), to change this edit:
/* Config: */ {
username: "bob"
servers: {
"example.com": {
proxy_pass: "http://example",
auth: "password" // Username is bob
}
}
}
/* Server/Subdomain: */ {
location: {
"/custom/path": {
proxy_pass: "http://proxy_pass:80",
// Any other option...
custom_css: "http://example.com/style.css"
}
}
}
or simplified:
/* Server/Subdomain: */ {
location: {
"/custom/path": "http://proxy_pass:80"
}
}
Retrieves Cloudflare's ips and creates a Nginx config to trust their CF-Connecting-IP
header as a source for a visitors real ip.
By default this data is cached for 7 days. Edit
Currently this only updates when the container starts.
// config.js
module.exports = {
cloudflare: true,
servers: {}
};
This allows customizing the access log format. For variables see here
// config.js
module.exports = {
nginx: {
log: "$time_local $remote_addr"
}
};
For every proxy_pass
's hostname a DNS lookup is performed to test if it can be found in upstream.
If the dns lookup fails the container exits with an error: Could not resolve: host
.
This check is useful because otherwise Nginx will exit later.
To disable this check, set the following variable:
DONT_CHECK_HOSTS="true" # Default: false
When nginx encounters a proxy_pass
directive it does a DNS lookup.
If this fails Nginx will exit / wont start.
To allow starting you can set:
DONT_EXIT_NO_UPSTREAM="true" # Default: false
Disables the actual downloading of the files specified using custom_css
or custom_js
.
Mainly useful for testing in this workflow
DONT_DOWNLOAD_FILES="true" # Default: false
The Cloudflare Option caches the list of ip addresses. By default for 7 days. To customize this duration set the following variable in milliseconds:
CLOUDFLARE_CACHE_DURATION="2419200000" # Default: 604800000
The default size is 2048, which should be secure enough. If you make this value larger it will take longer to create.
Note: if you want to change the size, you should also delete your old dhparam.pem otherwise it won't be recreated.
DHPARAM_SIZE="2048"
If you have a domain without or with expired certificates this container will request certificates for that domain.
To disable this you can set the following:
DISABLE_CERTBOT="true"
A domain without certificates will not be created (because Nginx will throw an error on launch)
If you would like to create configs even when it has missing certificate files:
ENABLE_CONFIG_MISSING_CERTS="true"
This email is used to request certificates using certbot.
Letsencrypt will send you an email when a certificate expires soon.
In the future an option will be added to register without email using certbot's --register-unsafely-without-email
. More info here
CERTBOT_EMAIL="[email protected]"
When using Letsencrypt's production environment to test things out, there is a high chance you will be running against rate limits.
In order to use the staging environment:
STAGING="true"
By default the container requests ECDSA certificates, which uses a newer encryption algorithm than RSA certificates.
If you still want RSA certificates:
Note: You might need to wait until your certificates expire in order to replace them automatically.
USE_ECDSA="false"
Watches the config file for changes, and live reloads when it changes.
Currently this can only be used with the standalone image Because the node process wil run forever so nginx won't be started
WATCH_CONFIG_FILE="true"
By default the log is formatted so every message starts at the same width.
If you want to disable the adding of spaces you can disable this by setting:
LOG_FORMAT_COLUMNS="false"
If you would like to disable certain elements in the log messages you can:
0.100s | [NCM] | [INFO] | [CONFIG] | Config is valid |
---|---|---|---|---|
Time | Name | Tag |
LOG_SHOW_TIME="false"
LOG_SHOW_NAME="false"
LOG_SHOW_TAG="false"
All values are set to there default, the commented value is the path inside the container.
You can update each path by setting the environment variable
# The config folder
CONFIG_PATH="./config" # /app/config
# If you want to point to a specific file instead of a folder
CONFIG_FILE= # Unset
# Nginx folder
NGINX_PATH="./nginx" # /etc/nginx
# Generated configs folder
NGINX_CONFIG_PATH="$NGINX_PATH/conf.d" # /etc/nginx/conf.d
# The cloudflare ip list config path
CLOUDFLARE_CONFIG_PATH="$NGINX_CONFIG_PATH/cloudflare.conf" # /etc/nginx/conf.d/cloudflare.conf
# Letsencrypt directory
LETSENCRYPT_PATH="/etc/letsencrypt"
# Diffie-Hellman file
DHPARAM_PATH="$LETSENCRYPT_PATH/dhparams/dhparams.pem" # /etc/letsencrypt/dhparams/dhparams.pem
# Data location, cloudflare ip cache, custom files and auth files
DATA_PATH="./data" # /app/data
# Stores custom files: CSS & JS
CUSTOM_FILES_PATH="$DATA_PATH/custom" # /app/data/custom
# Stores auth files, Stores the username:hashed_password
AUTH_PATH="$DATA_PATH/auth" # /app/data/auth
# Stores a cache of the cloudflare ip list.
STORE_PATH="$DATA_PATH/store.json" # /app/data/store.json
You can also run this container without Nginx builtin.
Keep in mind that this also means that NCM won't be able to reload nginx.
So you will have to this manually by running
docker exec nginx nginx -s reload
each time new configs are created
If you want you let NCM watch your config file for changes see here
In order to do so see this Docker Compose
In older versions this container used JonasAlfredsson/docker-nginx-certbot as a parent container. Since version v2.0.0 most of it's features like requesting certificates and generating Diffie-Hellman parameters are now included in this container.