├── LICENSE
├── README.md
├── directus
└── Dockerfile
├── docker-compose.yml
├── init-letsencrypt.sh
├── nginx
└── conf.d
│ └── default.conf
└── test-nginx.sh
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Emidio Torre
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Self Host Directus on Docker Compose with SSL certificates provided by Let's Encrypt
2 |
3 | ### Use this boilerplate at your own risk, not intended for production use.
4 |
5 |
6 |
7 | This is a boilerplate for self hosting [Directus](https://directus.io/) on [Docker Compose](https://docs.docker.com/compose/). It uses [Nginx](https://www.nginx.com/) as a reverse proxy and [Let's Encrypt](https://letsencrypt.org/) to provide SSL certificates.
8 |
9 | It is based on the official [Directus Docker image](https://hub.docker.com/r/directus/directus) and the [Nginx Docker image](https://hub.docker.com/_/nginx).
10 |
11 | The certbot configuration is taken from the [nginx-certbot repository](https://github.com/wmnnd/nginx-certbot) and the original article it's worth a read if you want to dive deeper: [Nginx and Let’s Encrypt with Docker in Less Than 5 Minutes](https://pentacent.medium.com/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71).
12 |
13 | The script to validate the nginx configuration is taken from this [article](https://dev.to/simdrouin/validate-your-nginx-configuration-files-easily-with-docker-4ihi).
14 |
15 |
16 |
17 | ### Prerequisites
18 |
19 | You need to have [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed on your machine.
20 |
21 |
22 |
23 | ### Getting Started
24 |
25 | clone this repo and edit the `docker-compose.yml` and the `nginx/default.conf` files to your needs.
26 |
27 | ```bash
28 | # provision certificates
29 | ./init-letsencrypt.sh
30 |
31 | # test nginx configuration
32 | ./test-nginx.sh
33 |
34 | # start the containers
35 | docker-compose up -d
36 | ```
37 |
38 | ### Contributing
39 |
40 | Feel free to open an issue or submit a pull request.
41 |
42 | Things I would like to add:
43 |
44 | - [ ] Make Certbot optional, to work in development and simplify testing the setup
45 | - [ ] Add a script to automate the initial configuration
46 | - [ ] Add a script to select the directus extensions to install
47 | - [ ] Support other services like [Laradock](https://github.com/laradock/laradock)
48 | - [ ] Add Rsync scripts to easily move configuration and data between different hosts
49 |
50 | ### Author
51 |
52 | - Emidio Torre [@emid_io](https://twitter.com/emid_io)
53 |
54 | ### License
55 |
56 | This project is open source and available under the [MIT License](LICENSE).
57 |
58 | ### Useful Commands
59 |
60 | ```bash
61 | # start the containers
62 | docker-compose up -d
63 |
64 | # stop the containers
65 | docker-compose down
66 |
67 | # rebuild the containers
68 | docker-compose up -d --build
69 |
70 | # list containers
71 | docker ps
72 |
73 | # list all containers
74 | docker ps -a
75 |
76 | # list images
77 | docker images
78 |
79 | # list volumes
80 | docker volume ls
81 |
82 | # remove all containers
83 | docker rm $(docker ps -a -q)
84 |
85 | # remove all images
86 | docker rmi $(docker images -q)
87 |
88 | # remove all volumes
89 | docker volume rm $(docker volume ls -q)
90 |
91 | # remove all unused containers, volumes, networks and images
92 | docker system prune
93 | ```
94 |
--------------------------------------------------------------------------------
/directus/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM directus/directus:9.18.1
2 |
3 | RUN npm install directus-extension-editorjs
4 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | nginx:
4 | image: nginx:1.23-alpine
5 | restart: unless-stopped
6 | ports:
7 | - 80:80
8 | - 443:443
9 | volumes:
10 | - ./nginx/conf.d:/etc/nginx/conf.d
11 | - ./certbot/conf:/etc/letsencrypt
12 | - ./certbot/www:/var/www/certbot
13 | networks:
14 | - directus
15 | depends_on:
16 | - directus
17 | #command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''
18 | certbot:
19 | image: certbot/certbot
20 | restart: unless-stopped
21 | volumes:
22 | - ./certbot/conf:/etc/letsencrypt
23 | - ./certbot/www:/var/www/certbot
24 | networks:
25 | - directus
26 | #entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
27 | database:
28 | container_name: database
29 | image: postgis/postgis:15-master
30 | # Required when running on platform other than amd64, like Apple M1/M2:
31 | # platform: linux/amd64
32 | volumes:
33 | - ./data:/var/lib/postgresql/data
34 | networks:
35 | - directus
36 | ports:
37 | - 5432:5432
38 | environment:
39 | POSTGRES_USER: "postgres"
40 | POSTGRES_PASSWORD: "INSERT_DB_PASSWORD"
41 | POSTGRES_DB: "db_name"
42 |
43 | cache:
44 | container_name: cache
45 | image: redis:6
46 | networks:
47 | - directus
48 |
49 | directus:
50 | container_name: directus
51 | build:
52 | context: "./directus/"
53 | dockerfile: Dockerfile
54 | ports:
55 | - 8055:8055
56 | volumes:
57 | # By default, uploads are stored in /directus/uploads
58 | # Always make sure your volumes matches the storage root when using
59 | # local driver
60 | - ./uploads:/directus/uploads
61 | # Make sure to also mount the volume when using SQLite
62 | # - ./database:/directus/database
63 | # If you want to load extensions from the host
64 | # - ./extensions:/directus/extensions
65 | networks:
66 | - directus
67 | depends_on:
68 | - cache
69 | - database
70 | environment:
71 | KEY: "INSERT_KEY"
72 | SECRET: "INSERT_SECRET"
73 | DB_CLIENT: "pg"
74 | DB_HOST: "database"
75 | DB_PORT: "5432"
76 | DB_DATABASE: "db_name"
77 | DB_USER: "postgres"
78 | DB_PASSWORD: "INSERT_DB_PASSWORD"
79 | CACHE_ENABLED: "true"
80 | CACHE_STORE: "redis"
81 | CACHE_REDIS: "redis://cache:6379"
82 | ADMIN_EMAIL: "INSERT_ADMIN_EMAIL"
83 | ADMIN_PASSWORD: "INSERT_ADMIN_PASSWORD"
84 |
85 | # Make sure to set this in production
86 | # (see https://docs.directus.io/self-hosted/config-options#general)
87 | PUBLIC_URL: "https://INSERT_PUBLIC_URL"
88 |
89 | networks:
90 | directus:
91 |
--------------------------------------------------------------------------------
/init-letsencrypt.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if ! [ -x "$(command -v docker-compose)" ]; then
4 | echo 'Error: docker-compose is not installed.' >&2
5 | exit 1
6 | fi
7 |
8 | domains=(example.com www.example.com) # Add your domain name(s) here
9 | rsa_key_size=4096
10 | data_path="./certbot" # Add your path to certbot data here
11 | email="admin@example.com" # Adding a valid address is strongly recommended
12 | staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
13 |
14 | if [ -d "$data_path" ]; then
15 | read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
16 | if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
17 | exit
18 | fi
19 | fi
20 |
21 |
22 | if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
23 | echo "### Downloading recommended TLS parameters ..."
24 | mkdir -p "$data_path/conf"
25 | curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
26 | curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
27 | echo
28 | fi
29 |
30 | echo "### Creating dummy certificate for $domains ..."
31 | path="/etc/letsencrypt/live/$domains"
32 | mkdir -p "$data_path/conf/live/$domains"
33 | docker-compose run --rm --entrypoint "\
34 | openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
35 | -keyout '$path/privkey.pem' \
36 | -out '$path/fullchain.pem' \
37 | -subj '/CN=localhost'" certbot
38 | echo
39 |
40 |
41 | echo "### Starting nginx ..."
42 | docker-compose up --force-recreate -d nginx
43 | echo
44 |
45 | echo "### Deleting dummy certificate for $domains ..."
46 | docker-compose run --rm --entrypoint "\
47 | rm -Rf /etc/letsencrypt/live/$domains && \
48 | rm -Rf /etc/letsencrypt/archive/$domains && \
49 | rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
50 | echo
51 |
52 |
53 | echo "### Requesting Let's Encrypt certificate for $domains ..."
54 | #Join $domains to -d args
55 | domain_args=""
56 | for domain in "${domains[@]}"; do
57 | domain_args="$domain_args -d $domain"
58 | done
59 |
60 | # Select appropriate email arg
61 | case "$email" in
62 | "") email_arg="--register-unsafely-without-email" ;;
63 | *) email_arg="--email $email" ;;
64 | esac
65 |
66 | # Enable staging mode if needed
67 | if [ $staging != "0" ]; then staging_arg="--staging"; fi
68 |
69 | docker-compose run --rm --entrypoint "\
70 | certbot certonly --webroot -w /var/www/certbot \
71 | $staging_arg \
72 | $email_arg \
73 | $domain_args \
74 | --verbose \
75 | --rsa-key-size $rsa_key_size \
76 | --agree-tos \
77 | --force-renewal" certbot
78 | echo
79 |
80 | echo "### Reloading nginx ..."
81 | docker-compose exec nginx nginx -s reload
82 |
--------------------------------------------------------------------------------
/nginx/conf.d/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | resolver 127.0.0.11 valid=30s ipv6=off; #use docker DNS server to resolve hosts that are starting
3 | listen 80;
4 | listen 443 ssl http2;
5 | server_name example.com; #change this to your domain
6 |
7 | #certbot will provide this certificates, you have to run init-letsencrypt.sh first
8 | ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
9 | ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
10 | include /etc/letsencrypt/options-ssl-nginx.conf;
11 | ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
12 |
13 | location /.well-known/acme-challenge/ {
14 | root /var/www/certbot;
15 | }
16 |
17 | location / {
18 | set $directus http://directus:8055;
19 | proxy_pass $directus;
20 | proxy_set_header Host $host;
21 | proxy_set_header X-Real-IP $remote_addr;
22 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
23 | proxy_set_header REMOTE-HOST $remote_addr;
24 | add_header X-Cache $upstream_cache_status;
25 |
26 | proxy_connect_timeout 30s;
27 | proxy_read_timeout 86400s;
28 | proxy_send_timeout 30s;
29 | proxy_http_version 1.1;
30 | proxy_set_header Upgrade $http_upgrade;
31 | proxy_set_header Connection "upgrade";
32 | }
33 |
34 | access_log /var/log/nginx/example.com.log;
35 | error_log /var/log/nginx/example.com.error.log;
36 | }
37 |
--------------------------------------------------------------------------------
/test-nginx.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rootPath=$1
3 | nginxVersion=1.23-alpine # Could also be passed as an argument using $2
4 |
5 | result=$(docker run --rm -t -a stdout --name my-nginx \
6 | -v $(pwd)/nginx/conf.d:/etc/nginx/conf.d:ro \
7 | -v $(pwd)/certbot/www:/var/www/certbot \
8 | -v $(pwd)/certbot/conf:/etc/letsencrypt \
9 | nginx:$nginxVersion nginx -c /etc/nginx/nginx.conf -t)
10 |
11 | # Look for the word successful and count the lines that have it
12 | # This validation could be improved if needed
13 | successful=$(echo $result | grep successful | wc -l)
14 |
15 | if [ $successful = 0 ]; then
16 | echo NGINX TEST FAILED
17 | echo "$result"
18 | exit 1
19 | else
20 | echo NGINX TEST SUCCESSFUL
21 | fi
22 |
--------------------------------------------------------------------------------