├── 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 | --------------------------------------------------------------------------------