├── .github ├── FUNDING.yml └── workflows │ └── CI.yml ├── .gitignore ├── .yamllint ├── LICENSE ├── README.md ├── ansible.cfg ├── default.config.yml ├── example.inventory ├── images ├── drupal-pi-model-2.jpg └── drupal-pi-model-3.jpg ├── main.yml ├── requirements.yml ├── reset.yml ├── tasks ├── docker-compose-setup.yml └── init.yml ├── templates ├── docker-compose.yml.j2 ├── drupal.conf.j2 └── proxy.conf.j2 └── tests ├── .vagrant └── rgloader │ └── loader.rb ├── README.md └── Vagrantfile /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # These are supported funding model platforms 3 | github: geerlingguy 4 | patreon: geerlingguy 5 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 'on': 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: "30 4 * * 0" 10 | 11 | jobs: 12 | 13 | lint: 14 | name: Lint 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Check out the codebase. 18 | uses: actions/checkout@v2 19 | 20 | - name: Set up Python 3. 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: '3.x' 24 | 25 | - name: Install test dependencies. 26 | run: pip3 install yamllint 27 | 28 | - name: Lint code. 29 | run: | 30 | yamllint . 31 | 32 | ci: 33 | name: CI 34 | runs-on: ubuntu-latest 35 | 36 | env: 37 | HOSTNAME: www.drupalpi.test 38 | MACHINE_NAME: drupalpi 39 | PLAYBOOK_DIR: /tmp/drupal-pi 40 | 41 | steps: 42 | - name: Check out the codebase. 43 | uses: actions/checkout@v2 44 | 45 | - name: Run container in detached state. 46 | run: >- 47 | docker run --detach --name drupalpi 48 | --add-host "${{ env.HOSTNAME }}$(echo -e "\t")${{ env.MACHINE_NAME }}":127.0.0.1 49 | --volume="${PWD}":${{ env.PLAYBOOK_DIR }}/:rw --privileged --volume=/var/lib/docker 50 | --volume=/sys/fs/cgroup:/sys/fs/cgroup:rw --cgroupns=host 51 | geerlingguy/docker-debian11-ansible:latest /lib/systemd/systemd 52 | 53 | - name: Copy inventory file into place. 54 | run: >- 55 | docker exec drupalpi cp ${{ env.PLAYBOOK_DIR }}/example.inventory ${{ env.PLAYBOOK_DIR }}/inventory 56 | 57 | - name: Install requirements. 58 | run: >- 59 | docker exec --tty drupalpi ansible-galaxy install -r ${{ env.PLAYBOOK_DIR }}/requirements.yml 60 | 61 | - name: Ansible syntax check. 62 | run: >- 63 | docker exec --tty drupalpi ansible-playbook -i ${{ env.PLAYBOOK_DIR }}/inventory ${{ env.PLAYBOOK_DIR }}/main.yml --syntax-check 64 | 65 | - name: Run the playbook. 66 | run: >- 67 | docker exec drupalpi 68 | env ANSIBLE_FORCE_COLOR=true 69 | ansible-playbook -i ${{ env.PLAYBOOK_DIR }}/inventory 70 | --connection=local 71 | --extra-vars "{ 72 | 'docker_version':'5:20.10.22~3-0~debian-bullseye', 73 | 'firewall_enable_ipv6':false 74 | }" 75 | ${{ env.PLAYBOOK_DIR }}/main.yml 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | .vagrant 3 | inventory 4 | config.yml 5 | hook-pre-tasks.yml 6 | hook-tasks.yml 7 | roles/* 8 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: 6 | max: 180 7 | level: warning 8 | 9 | ignore: | 10 | .github/stale.yml 11 | config.yml 12 | roles/geerlingguy.* 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeff Geerling 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drupal Pi 2 | 3 | [![CI](https://github.com/geerlingguy/drupal-pi/actions/workflows/CI.yml/badge.svg)](https://github.com/geerlingguy/drupal-pi/actions/workflows/CI.yml) 4 | 5 | **Drupal on Docker on a Raspberry Pi** 6 | 7 |

Drupal 8 on a Raspberry Pi

8 | 9 | This project is an offshoot of the [Rasbperry Pi Dramble](https://github.com/geerlingguy/raspberry-pi-dramble) project, which helps install Drupal on a cluster ('Bramble') of Raspberry Pi computers. 10 | 11 | This playbook/project makes setting up Drupal on a _single_ Raspberry Pi a very easy/simple operation using Nginx and Docker Compose. 12 | 13 | ## Set up the Raspberry Pi 14 | 15 | Drupal requires as good a Raspberry Pi as you can afford. While Drupal will run okay on any Raspberry Pi, it's best to use a model 2 B or later (newer models have a snappy four-core processor and at least 1GB RAM). 16 | 17 | Once you have your Raspberry Pi and a good microSD card (the fastest/best one you can get—see [microSD Card Benchmarks](http://www.pidramble.com/wiki/benchmarks/microsd-cards)!), you will need to do a few things to set up the Raspberry Pi and get it ready to run Drupal. 18 | 19 | ### Set up on Raspberry Pi with Raspbian / GUI 20 | 21 | These directions assume you're working directly on your Raspberry Pi, running Raspbian, with a keyboard and monitor attached: 22 | 23 | 1. Download the latest 'Raspbian' image from the [Raspberry Pi Downloads page](https://www.raspberrypi.org/downloads/)†. 24 | 1. Follow the [image installation guide](https://www.raspberrypi.org/documentation/installation/installing-images/README.md) to transfer the image to your microSD card: 25 | 1. Unmount the microSD card: `diskutil unmountDisk /dev/disk2` 26 | 1. Write the image to the microSD card: `pv yyyy-mm-dd-raspbian-stretch.img | sudo dd bs=1m of=/dev/rdisk2` 27 | 1. Once Raspbian is loaded on the card, insert the card in your Pi, and plug in your Pi to boot it up. 28 | 1. Boot up the Raspberry Pi. Once booted, open the "Raspberry Pi Configuration" tool in Menu > Preferences. 29 | 1. Change the pi user account password. 30 | 1. Click OK, then reboot the Raspberry Pi. 31 | 1. Once rebooted, connect the Pi to your local network either via WiFi or wired ethernet. 32 | 1. Open the Terminal application (in the launcher or in Menu > Accessories > Terminal). 33 | 1. Install Ansible: `sudo apt-get update && sudo apt-get install -y python3-dev python3-pip libyaml-dev libffi-dev && sudo pip3 install ansible` 34 | 1. Test the Ansible installation: `ansible --version` (should output the Ansible version). 35 | 36 | *† If you plan on using your Pi as a headless Drupal server, you don't need all the extra software included with the default Raspbian image. I recommend you use the official 'Raspbian Lite' image instead; see the next section.* 37 | 38 | ### Set up on Raspberry Pi with Raspbian Lite / CLI 39 | 40 | These directions assume you're working either directly on your Raspberry Pi, running Raspbian Lite, or remotely logged into the Pi via SSH: 41 | 42 | 1. Download the latest 'Raspbian Lite' image from the [Raspberry Pi Downloads page](https://www.raspberrypi.org/downloads/)†. 43 | 1. Follow the [image installation guide](https://www.raspberrypi.org/documentation/installation/installing-images/README.md) to transfer the image to your microSD card: 44 | 1. Unmount the microSD card: `diskutil unmountDisk /dev/disk2` 45 | 1. Write the image to the microSD card: `pv yyyy-mm-dd-raspbian-stretch-lite.img | sudo dd bs=1m of=/dev/rdisk2` 46 | 1. Ensure SSH is enabled by adding an 'ssh' file to the boot volume: `touch /Volumes/boot/ssh` 47 | 1. If you need to enable WiFi headlessly, add a `wpa_supplicant.conf` file to the boot volume as well: 48 | 1. Create the file: `touch /Volumes/boot/wpa_supplicant.conf` 49 | 1. Add the contents: 50 | ``` 51 | ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev 52 | update_config=1 53 | country=US 54 | 55 | network={ 56 | ssid="your-network-name" 57 | psk="your-network-password" 58 | key_mgmt=WPA-PSK 59 | } 60 | ``` 61 | 1. Once Raspbian Lite is loaded on the card, insert the card in your Pi, and plug in your Pi to boot it up. 62 | 1. Boot up the Raspberry Pi. Once booted, log in (default username is `pi` and default password is `raspberry`), and run `sudo raspi-config`. 63 | 1. Set a better password for the Pi's default user account. 64 | 1. Scroll down to 'Finished', hit return, and reboot the Raspberry Pi. 65 | 1. Once rebooted, connect the Pi to your local network either via [WiFi](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-3-network-setup/setting-up-wifi-with-occidentalis) or wired ethernet. 66 | 1. Log back in (either on the Pi directly or via SSH). 67 | 1. Install Ansible: `sudo apt-get update && sudo apt-get install -y python3-dev python3-pip libyaml-dev libffi-dev git && sudo pip3 install ansible` 68 | 1. Test the Ansible installation: `ansible --version` (should output the Ansible version). 69 | 70 | ## Install LEMP software stack and Drupal with Ansible and Docker 71 | 72 | ### Installing using the Raspberry Pi 73 | 74 | You need to download this repository to the Pi and run the included playbook to install and configure everything. 75 | 76 | 1. Clone the `drupal-pi` project: `git clone https://github.com/geerlingguy/drupal-pi.git && cd drupal-pi` 77 | 1. Copy `example.inventory` to `inventory`. 78 | 1. (Optional) Create a `config.yml` and override any settings from `default.config.yml` as needed. 79 | 1. Install required Ansible roles: `ansible-galaxy install -r requirements.yml` 80 | 1. Run the Ansible playbook: `ansible-playbook -c local main.yml` 81 | 82 | After a few minutes, the playbook should complete successfully, and you should have Drupal running on your Raspberry Pi, accessible via `http://localhost/` 83 | 84 | To be able to access the site from other computers on your network (e.g. by accessing `http://www.drupalpi.test/`, [add an entry to your local hosts file](http://www.rackspace.com/knowledge_center/article/how-do-i-modify-my-hosts-file) like `[ip-of-raspberry-pi] www.drupalpi.test`. 85 | 86 | > **NOTE**: For best security, you should create a `config.yml` file and at least override any `password` settings from the `default.config.yml` file (e.g. `drupal_database_password`). 87 | 88 | ### Installing using another host with Ansible installed 89 | 90 | You can run the Ansible playbook from another host (instead of from within the VM—this also allows you to do everything without installing `pip` and `ansible` on the Raspberry Pi itself!). 91 | 92 | 1. Change the `inventory` file to use the Pi's IP address instead of `127.0.0.1`. 93 | 1. Make sure you have your SSH private key configured for the `pi` account on the Pi (I use `ssh-copy-id` to copy my ID to the Pi). 94 | 1. Install required Ansible roles: `ansible-galaxy install -r requirements.yml` 95 | 1. Run the Ansible playbook: `ansible-playbook main.yml` 96 | 97 | Note: If you have a headless Raspberry Pi and would like to find it's IP address, one way of doing so is to use a tool like [Fing](https://www.fingbox.com/features)). 98 | 99 | ## Advanced Usage 100 | 101 | ### Adding your own Ansible tasks 102 | 103 | If you need to add some additional automation, there is a concept of 'hook' files which will get called at different stages of the build if they are present (in the root directory of this project): 104 | 105 | - `hook-pre-tasks.yml`: Gets called in the pre_tasks section of the playbook before any Ansible roles or tasks are applied. 106 | - `hook-tasks.yml`: Gets called at the start of the tasks section of the playbook (after roles are applied). 107 | 108 | One use of these hooks would be to do extra steps to deploy your _own_ container and codebase, without having to use a private registry. See an example which I use to build and deploy one of my Drupal codebases: [`hook-tasks.yml` example for Drupal for Kubernetes](https://github.com/geerlingguy/drupal-pi/issues/36#issuecomment-468519984). 109 | 110 | ### Updating your Pi (for future versions of Drupal Pi) 111 | 112 | If you need to update Drupal Pi, do the following: 113 | 114 | 1. cd into the project directory: `cd /path/to/drupal-pi` 115 | 1. Pull the latest changes: `git pull` 116 | 1. Update all required Ansible roles (and install new ones): `sudo ansible-galaxy install -r requirements.yml --force` 117 | 1. Run the Ansible playbook: `ansible-playbook -i inventory -c local main.yml` 118 | 119 | _Note_: Remove `-c local` if running from another host. 120 | 121 | ### Resetting the Drupal Install 122 | 123 | There is a `reset.yml` playbook included that will reset the environment so you can install a fresh copy of Drupal. To run the playbook, enter the following command in the same directory as this README: 124 | 125 | ansible-playbook -i inventory -c local reset.yml 126 | 127 | _Note_: Remove `-c local` if running from another host. 128 | 129 | After it finishes resetting the environment, you can run the `main.yml` playbook again to rebuild the Drupal site. 130 | 131 | ### Enabling Proxy Caching 132 | 133 | The Drupal Pi includes a basic proxy cache configuration which uses Nginx to reverse proxy and cache all requests which do not include a Drupal session cookie. This can increase the speed of page delivery by 60x or more, meaning your little Pi could serve up more traffic than most home Internet uplinks could handle! 134 | 135 | To enable Nginx's proxy cache, set the following value in your `config.yml` and run the main playbook: 136 | 137 | nginx_proxy_cache: true 138 | 139 | ### Using Drupal Pi as a load balancer for Pi Dramble 140 | 141 | This project can also switch from running a site locally to being used as a load balancer for the Pi Dramble Cluster. All you have to do is set `nginx_use_as_lb: true` in your `config.yml`, make sure all the Pis which are responding to requests are in the `nginx_lb_backends` list, and run the playbook to redeploy the Nginx configuration. 142 | 143 | Then point the domain you would normally point at the Pi Dramble cluster (e.g. `cluster.pidramble.test`) at the IP of the single Drupal Pi instead! 144 | 145 | ## Author 146 | 147 | This project was started in 2015 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). 148 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | roles_path = ./roles 3 | nocows = 1 4 | retry_files_enabled = False 5 | stdout_callback = yaml 6 | # bin_ansible_callbacks = True 7 | inventory = inventory 8 | 9 | [ssh_connection] 10 | pipelining = True 11 | control_path = /tmp/ansible-ssh-%%h-%%p-%%r 12 | -------------------------------------------------------------------------------- /default.config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Raspberry Pi configuration. 3 | drupal_pi_processor_count: 4 4 | drupal_pi_app_directory: /opt/drupal-pi 5 | 6 | # Security configuration. 7 | security_sudoers_passwordless: ['pi'] 8 | firewall_allowed_tcp_ports: 9 | - 22 10 | - 80 11 | - 443 12 | 13 | # Drupal configuration. 14 | drupal_url: www.drupalpi.test 15 | drupal_docker_image_armv7: 'geerlingguy/drupal:latest-arm32v7' 16 | drupal_docker_image_arm64: 'geerlingguy/drupal:latest-arm64' 17 | drupal_docker_image: "{{ drupal_docker_image_armv7 if 'armv7' in ansible_architecture else drupal_docker_image_arm64 }}" 18 | drupal_database_password: supersecure 19 | drupal_files_directory_host: /var/drupal/files 20 | drupal_files_directory_container: /var/www/html/sites/default/files 21 | drupal_files_directory_owner: "33" 22 | drupal_hash_salt: JcWFc2Z29LQIAeorS0IL9qB4SxOCjiYMd909AM3E2U 23 | drupal_pidramble_analytics_enabled: 'false' 24 | drupal_account_name: admin 25 | drupal_account_pass: admin 26 | drupal_download_if_not_present: 'true' 27 | 28 | # MySQL configuration. 29 | mysql_docker_image: 'webhippie/mariadb:latest' 30 | mysql_root_password: rootpasswordhere 31 | 32 | # Nginx configuration. 33 | nginx_worker_processes: "{{ drupal_pi_processor_count }}" 34 | nginx_worker_connections: "512" 35 | nginx_client_max_body_size: "64m" 36 | nginx_keepalive_timeout: "65" 37 | nginx_remove_default_vhost: true 38 | nginx_vhosts: [] 39 | nginx_access_log: "off" 40 | nginx_proxy_pass: http://127.0.0.1:8080/ 41 | nginx_proxy_cache: false 42 | nginx_proxy_cache_path: "/var/cache/nginx keys_zone=cache:32m max_size=1g inactive=15m" 43 | 44 | # Python configuration. 45 | pip_package: python3-pip 46 | pip_executable: pip3 47 | docker_pip_executable: '{{ pip_executable }}' 48 | 49 | # Nginx load balancing configuration (disabled by default). 50 | nginx_use_as_lb: false 51 | nginx_lb_backends: 52 | - '10.0.100.62' 53 | - '10.0.100.63' 54 | - '10.0.100.64' 55 | -------------------------------------------------------------------------------- /example.inventory: -------------------------------------------------------------------------------- 1 | [pi] 2 | 127.0.0.1 ansible_python_interpreter=/usr/bin/python3 3 | 4 | # Comment the default localhost line above, and uncomment the line below 5 | # (replacing the IP with your Pi's IP address) if running the playbook from a 6 | # separate workstation. 7 | #10.0.100.20 ansible_user=pi ansible_python_interpreter=/usr/bin/python3 8 | -------------------------------------------------------------------------------- /images/drupal-pi-model-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geerlingguy/drupal-pi/79582cbf4aebebabe312e90c0dc1b48542533f2d/images/drupal-pi-model-2.jpg -------------------------------------------------------------------------------- /images/drupal-pi-model-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geerlingguy/drupal-pi/79582cbf4aebebabe312e90c0dc1b48542533f2d/images/drupal-pi-model-3.jpg -------------------------------------------------------------------------------- /main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: pi 3 | become: true 4 | 5 | vars_files: 6 | - default.config.yml 7 | 8 | pre_tasks: 9 | - name: Include config override file, if it exists. 10 | include_vars: "{{ item }}" 11 | with_fileglob: 12 | - config.yml 13 | tags: ['always'] 14 | 15 | - name: Include hook-pre-tasks.yml if it exists. 16 | include_tasks: "{{ item }}" 17 | with_first_found: 18 | - files: 19 | - hook-pre-tasks.yml 20 | skip: true 21 | tags: ['always'] 22 | 23 | - import_tasks: tasks/init.yml 24 | 25 | roles: 26 | - geerlingguy.security 27 | - geerlingguy.firewall 28 | - geerlingguy.git 29 | - name: geerlingguy.nginx 30 | tags: ['nginx'] 31 | - geerlingguy.pip 32 | - geerlingguy.docker 33 | 34 | tasks: 35 | - name: Include hook-tasks.yml if it exists. 36 | include_tasks: "{{ item }}" 37 | with_first_found: 38 | - files: 39 | - hook-tasks.yml 40 | skip: true 41 | 42 | - import_tasks: tasks/docker-compose-setup.yml 43 | 44 | - name: Copy nginx configuration into place. 45 | template: 46 | src: "templates/{{ item.src }}" 47 | dest: "{{ item.dest }}" 48 | mode: 0644 49 | notify: restart nginx 50 | with_items: 51 | - src: proxy.conf.j2 52 | dest: /etc/nginx/conf.d/proxy.conf 53 | - src: drupal.conf.j2 54 | dest: /etc/nginx/sites-enabled/drupal.conf 55 | tags: ['nginx'] 56 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - src: geerlingguy.security 3 | - src: geerlingguy.firewall 4 | - src: geerlingguy.git 5 | - src: geerlingguy.nginx 6 | - src: geerlingguy.pip 7 | - src: geerlingguy.docker 8 | -------------------------------------------------------------------------------- /reset.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: pi 3 | become: true 4 | 5 | vars_files: 6 | - default.config.yml 7 | 8 | tasks: 9 | - name: Include config override file, if it exists. 10 | include_vars: "{{ item }}" 11 | with_fileglob: 12 | - config.yml 13 | 14 | - name: Completely destroy docker-compose environment. 15 | command: > 16 | docker-compose down -v 17 | chdir={{ drupal_pi_app_directory }} 18 | 19 | - name: Delete the contents of the host Drupal files directory. 20 | shell: > 21 | rm -rf {{ drupal_files_directory_host }}/* 22 | warn=false 23 | changed_when: true 24 | 25 | # Can't use docker_service module until the following issue is resolved: 26 | # https://github.com/ansible/ansible/issues/26937 27 | - name: Rebuild the docker-compose environment. 28 | command: > 29 | docker-compose up -d --remove-orphans 30 | chdir={{ drupal_pi_app_directory }} 31 | -------------------------------------------------------------------------------- /tasks/docker-compose-setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure Drupal Pi app directory exists. 3 | file: 4 | path: "{{ drupal_pi_app_directory }}" 5 | state: directory 6 | 7 | - name: Ensure Drupal files directory exists. 8 | file: 9 | path: "{{ drupal_files_directory_host }}" 10 | state: directory 11 | owner: "{{ drupal_files_directory_owner }}" 12 | group: "{{ drupal_files_directory_owner }}" 13 | 14 | - name: Ensure Docker Compose file is present. 15 | template: 16 | src: templates/docker-compose.yml.j2 17 | dest: "{{ drupal_pi_app_directory }}/docker-compose.yml" 18 | mode: 0644 19 | register: docker_compose_file_result 20 | 21 | - name: Stop all Docker containers if docker-compose.yml has changed. 22 | command: > 23 | docker compose stop 24 | chdir={{ drupal_pi_app_directory }} 25 | when: docker_compose_file_result.changed 26 | 27 | # Can't use docker_service module until the following issue is resolved: 28 | # https://github.com/ansible/ansible/issues/26937 29 | - name: Bring up the Docker containers. 30 | command: > 31 | docker compose up -d --remove-orphans 32 | chdir={{ drupal_pi_app_directory }} 33 | -------------------------------------------------------------------------------- /tasks/init.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update apt cache if needed. 3 | apt: 4 | update_cache: true 5 | cache_valid_time: 900 6 | 7 | - name: Ensure /usr/local/bin exists. 8 | file: 9 | path: /usr/local/bin 10 | state: directory 11 | mode: 0775 12 | 13 | - name: Ensure auth.log file is present. 14 | copy: 15 | dest: /var/log/auth.log 16 | content: "" 17 | force: false 18 | 19 | - name: Ensure dependencies are installed. 20 | apt: 21 | name: 22 | - curl 23 | - openssh-server 24 | - openssh-client 25 | - libffi-dev 26 | - libssl-dev 27 | # For some flavors of Debian without cron installed by default. 28 | - cron 29 | state: present 30 | 31 | # TODO: We can eventually just default to geerlingguy/drupal:latest once this 32 | # issue is resolved: https://github.com/geerlingguy/drupal-container/issues/25 33 | - name: Override Drupal Docker image if not on arm and default is set. 34 | set_fact: 35 | drupal_docker_image: 'geerlingguy/drupal:latest' 36 | when: 37 | - ansible_architecture is not search('armv7') 38 | - ansible_architecture is not search('aarch64') 39 | - "'geerlingguy/drupal:latest-' in drupal_docker_image" 40 | -------------------------------------------------------------------------------- /templates/docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | services: 3 | drupal: 4 | image: {{ drupal_docker_image }} 5 | container_name: drupal 6 | environment: 7 | DRUPAL_DATABASE_HOST: 'mysql' 8 | DRUPAL_DATABASE_PORT: '3306' 9 | DRUPAL_DATABASE_NAME: 'drupal' 10 | DRUPAL_DATABASE_USERNAME: 'drupal' 11 | DRUPAL_DATABASE_PASSWORD: '{{ drupal_database_password }}' 12 | DRUPAL_HASH_SALT: '{{ drupal_hash_salt }}' 13 | DRUPAL_DOWNLOAD_IF_NOT_PRESENT: '{{ drupal_download_if_not_present }}' 14 | DRUPAL_DOWNLOAD_VERIFY_CERT: 'false' 15 | PIDRAMBLE_ANALYTICS_ENABLED: '{{ drupal_pidramble_analytics_enabled }}' 16 | volumes: 17 | - {{ drupal_files_directory_host }}:{{ drupal_files_directory_container }}:rw 18 | ports: 19 | - "8080:80" 20 | restart: always 21 | 22 | mysql: 23 | image: {{ mysql_docker_image }} 24 | container_name: drupal-mysql 25 | environment: 26 | MYSQL_ROOT_PASSWORD: '{{ mysql_root_password }}' 27 | MYSQL_DATABASE: drupal 28 | MYSQL_USER: drupal 29 | MYSQL_PASSWORD: '{{ drupal_database_password }}' 30 | 31 | MARIADB_ROOT_PASSWORD: '{{ mysql_root_password }}' 32 | MARIADB_DATABASE: drupal 33 | MARIADB_USERNAME: drupal 34 | MARIADB_PASSWORD: '{{ drupal_database_password }}' 35 | ports: 36 | - "3306:3306" 37 | restart: always 38 | volumes: 39 | - /var/lib/mysql 40 | -------------------------------------------------------------------------------- /templates/drupal.conf.j2: -------------------------------------------------------------------------------- 1 | {% if nginx_proxy_cache %} 2 | map $http_cookie $drupal_logged_in { 3 | default 0; 4 | ~SESS 1; 5 | } 6 | {% endif %} 7 | 8 | {% if not nginx_use_as_lb %} 9 | {# Normal configuration for single Drupal Pi. #} 10 | server { 11 | listen 80 default; 12 | server_name {{ drupal_url }}; 13 | 14 | gzip on; 15 | gzip_static on; 16 | 17 | {% if nginx_proxy_cache %} 18 | proxy_cache cache; 19 | proxy_cache_key $scheme$host$uri$is_args$args; 20 | {% endif %} 21 | 22 | access_log {{ nginx_access_log }}; 23 | 24 | location / { 25 | proxy_pass {{ nginx_proxy_pass }}; 26 | include /etc/nginx/conf.d/proxy.conf; 27 | } 28 | } 29 | {% else %} 30 | {# Configuration for using Drupal Pis as load balancer for Pi Dramble. #} 31 | upstream backend { 32 | {% for host in nginx_lb_backends %} 33 | server {{ host }} max_fails=3; 34 | {% endfor %} 35 | } 36 | 37 | server { 38 | listen 80 default; 39 | 40 | gzip on; 41 | gzip_static on; 42 | 43 | {% if nginx_proxy_cache %} 44 | proxy_cache cache; 45 | proxy_cache_key $scheme$host$uri$is_args$args; 46 | {% endif %} 47 | 48 | location / { 49 | proxy_pass http://backend; 50 | include /etc/nginx/conf.d/proxy.conf; 51 | } 52 | } 53 | {% endif %} 54 | -------------------------------------------------------------------------------- /templates/proxy.conf.j2: -------------------------------------------------------------------------------- 1 | proxy_set_header X-Forwarded-For $remote_addr; 2 | proxy_set_header Host $host; 3 | proxy_redirect off; 4 | 5 | {% if nginx_proxy_cache %} 6 | proxy_cache_bypass $drupal_logged_in $http_cache_control; 7 | proxy_no_cache $drupal_logged_in $arg_nocache$arg_page; 8 | proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; 9 | add_header X-Proxy-Cache $upstream_cache_status; 10 | {% endif %} 11 | -------------------------------------------------------------------------------- /tests/.vagrant/rgloader/loader.rb: -------------------------------------------------------------------------------- 1 | # This file loads the proper rgloader/loader.rb file that comes packaged 2 | # with Vagrant so that encoded files can properly run with Vagrant. 3 | 4 | if ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] 5 | require File.expand_path( 6 | "rgloader/loader", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]) 7 | else 8 | raise "Encoded files can't be read outside of the Vagrant installer." 9 | end 10 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Vagrant testing configuration 2 | 3 | This Vagrantfile can be used to test the Drupal Pi configuration locally if you do not have access to a Raspberry Pi. 4 | 5 | To test locally: 6 | 7 | 1. Install required Ansible roles (up one directory): `ansible-galaxy install -r requirements.yml --force` 8 | 1. Start and provision a local VM (in this directory): `vagrant up` 9 | 1. Visit `http://local.drupalpi.test/` and follow the Drupal installer steps to install Drupal. 10 | -------------------------------------------------------------------------------- /tests/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | VAGRANTFILE_API_VERSION = '2' 4 | 5 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 6 | config.vm.hostname = 'local.drupalpi.test' 7 | config.vm.network :private_network, ip: '192.168.29.42' 8 | 9 | # VirtualBox configuration. 10 | config.vm.provider :virtualbox do |v| 11 | v.memory = 1024 12 | v.cpus = 1 13 | v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 14 | v.customize ["modifyvm", :id, "--ioapic", "on"] 15 | v.customize ['modifyvm', :id, '--audio', 'none'] 16 | end 17 | 18 | # SSH options. 19 | config.ssh.insert_key = false 20 | config.ssh.forward_agent = true 21 | 22 | # Vagrant box. 23 | config.vm.box = 'geerlingguy/debian11' 24 | 25 | config.vm.provision "ansible" do |ansible| 26 | ansible.playbook = "../main.yml" 27 | ansible.inventory_path = "inventory" 28 | ansible.limit = "all" 29 | ansible.compatibility_mode = "2.0" 30 | end 31 | 32 | # Set the name of the VM. See: http://stackoverflow.com/a/17864388/100134 33 | config.vm.define 'drupal-pi' do |t| 34 | end 35 | 36 | end 37 | --------------------------------------------------------------------------------