Skip to content

Instantly share code, notes, and snippets.

@dievardump
Last active December 20, 2023 16:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dievardump/2c655e01e1f74108d46d5884af7de8c0 to your computer and use it in GitHub Desktop.
Save dievardump/2c655e01e1f74108d46d5884af7de8c0 to your computer and use it in GitHub Desktop.
Creating a server (VPS, EC2, anything you have access to) that can handle several VIRTUAL_HOST without the hassle of managing `sites-enabled` etc...

Description

This is a simple configuration that uses docker images nginxproxy/nginx-proxy & nginxproxy/acme-companion to easily create SSL-enabled websites on any server.

nginx-proxy is an image that automatically sets up a proxy based on nginx. It watches new containers creation / start and, if the container has a VIRTUAL_HOST environment var, will automatically create the configuration for it (no more site-enabled site-available hassle). If there is only one port exposed on the container, nginx-proxy will automatically proxy to that port.

It's highly configurable (globally, per virtual host, per location, per...)

acme-companion will automatically generate the SSL certificates using let's encrypt if the container has a LETSENCRYPT_HOST env var.

How does it work

First, install docker if not already done

What I usually do (using the web user but you can change for your own or just change the directory):

mkdir /home/web/www
mkdir /home/web/www/conf
mkdir /home/web/www/conf/conf.d
mkdir /home/web/www/conf/vhost.d
mkdir /home/web/www/conf/htpasswd
mkdir /home/web/www/websites

touch /home/web/www/docker-compose.yml

then I save the content of docker-compose-proxy.yml in it

I create the nginx-proxy network that allows the nginx-proxy container to find the containers it needs to connect to the web

docker network create nginx-proxy

then I start the proxy

cd /home/web/www/
docker compose up -d

Adding a website

Whenever I need to add a website (for this example myWesbite.xyz):

mkdir /home/web/www/websites/myWesbite.xyz
mkdir /home/web/www/websites/myWesbite.xyz/public
touch /home/web/www/websites/myWesbite.xyz/docker-compose.yml

Content of docker-compose.yml varies depending on your need.

Whenever the content is set:

cd /home/web/www/websites/myWesbite.xyz
docker compose up -d
version: "3"
services:
postgres:
image: postgres:16
container_name: myWebsite_postgres
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=E8FiIVyP5DPiHLwCRMXPHGZYa # change that password
- POSTGRES_DB=postgres
user: "1001:1001" # value depends on your current user
volumes:
- ./pg-data:/var/lib/postgresql/data
expose:
- 5432
networks:
- backend # network allows container to speak together. here it allows the node & adminer container to access this one
myWebsiteApi:
restart: unless-stopped
environment:
- VIRTUAL_HOST=api.myWebsite.xyz
- LETSENCRYPT_HOST=api.myWebsite.xyz
# change that to be the same as postgres conf, you can also use a .env file
- DATABASE_URL=postgresql://postgres:E8FiIVyP5DPiHLwCRMXPHGZYa@postgres:5432/postgres?schema=public
image: 'node:latest'
container_name: myWebsite_api
command: 'node index.js'
user: 'node'
working_dir: /home/node/app
expose:
- 3000
volumes:
- ./public:/home/node/app
depends_on:
- postgres
networks:
- backend # allows to communicate with postgres
- nginx-proxy # allows nginx-proxy to detect it and make it web accessible
myWebsite:
container_name: myWebsite
image: nginx
restart: always
environment:
- VIRTUAL_HOST=myWebsite.xyz
- LETSENCRYPT_HOST=myWebsite.xyz
volumes:
- ./public:/usr/share/nginx/html:ro
networks:
backend:
driver: bridge
default:
external:
name: nginx-proxy
version: "3"
services:
postgres:
image: postgres:16
container_name: myWebsite_postgres
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=E8FiIVyP5DPiHLwCRMXPHGZYa # change that password
- POSTGRES_DB=postgres
user: "1001:1001" # value depends on your current user
volumes:
- ./pg-data:/var/lib/postgresql/data
expose:
- 5432
networks:
- backend # network allows container to speak together. here it allows the node & adminer container to access this one
myWebsite:
restart: unless-stopped
environment:
- VIRTUAL_HOST=myWebsite.xyz
- LETSENCRYPT_HOST=myWebsite.xyz
# change that to be the same as postgres conf, you can also use a .env file
- DATABASE_URL=postgresql://postgres:E8FiIVyP5DPiHLwCRMXPHGZYa@postgres:5432/postgres?schema=public
image: 'node:latest'
container_name: myWebsite
command: 'node index.js'
user: 'node'
working_dir: /home/node/app
expose:
- 3000
volumes:
- ./public:/home/node/app
depends_on:
- postgres
networks:
- backend # allows to communicate with postgres
- nginx-proxy # allows nginx-proxy to detect it and make it web accessible
# to protect this adminer, you need to create a file: `home/web/www/conf/htpasswd/adminer.myWebsite.xyz
# in this file you can add any htpasswd configuration you like (https://www.web2generators.com/apache-tools/htpasswd-generator)
# then your restart nginx-proxy "docker restart nginx-proxy"
# now this URL is htpasswd protected
# you can then connect to postgres using its container name and the user/passwd you set up in the postgres container
adminer:
image: adminer
container_name: adminer
restart: unless-stopped
environment:
- VIRTUAL_HOST=adminer.myWebsite.xyz
- LETSENCRYPT_HOST=adminer.myWebsite.xyz
networks:
- backend
- nginx-proxy
networks:
backend:
driver: bridge
default:
external:
name: nginx-proxy
version: "3"
services:
myWebsite:
restart: unless-stopped
environment:
- VIRTUAL_HOST=myWebsite.xyz
- LETSENCRYPT_HOST=myWebsite.xyz
image: 'node:latest'
container_name: myWebsite
command: 'node index.js'
user: 'node'
working_dir: /home/node/app
expose:
- 3000
volumes:
- ./public:/home/node/app
networks:
default:
external:
name: nginx-proxy
# /home/web/www/docker-compose.yml
version: '3'
services:
nginx-proxy:
image: nginxproxy/nginx-proxy
container_name: nginx-proxy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./conf/htpasswd:/etc/nginx/htpasswd:ro
- ./conf/conf.d/:/etc/nginx/conf.d
- ./conf/vhost.d/:/etc/nginx/vhost.d
networks:
- nginx-proxy
acme-companion:
image: nginxproxy/acme-companion
container_name: nginx-proxy-acme
restart: always
environment:
- DEFAULT_EMAIL={YOUR_EMAIL}
volumes_from:
- nginx-proxy
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- nginx-proxy
networks:
nginx-proxy:
external: true
volumes:
html:
certs:
acme:
version: "3"
services:
myWebsite:
container_name: myWebsite
image: nginx
restart: always
environment:
- VIRTUAL_HOST=myWebsite.xyz
- LETSENCRYPT_HOST=myWebsite.xyz
volumes:
- ./public:/usr/share/nginx/html:ro
networks:
default:
name: nginx-proxy
external: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment