Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions docker/Dockerfile.churchcrm-fpm-php8
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# PHP-FPM Dockerfile for ChurchCRM (nginx deployments)
#
# Use this image with the nginx configuration in docker/nginx/default.conf.
# For the Apache-based development/CI setup, see Dockerfile.churchcrm-apache-php8.
#
# Build:
# docker build -f Dockerfile.churchcrm-fpm-php8 -t churchcrm/crm:php8-fpm .
#
# Required PHP extensions for ChurchCRM:
# bcmath, curl, exif, gd, gettext, iconv, intl, mbstring, mysqli,
# opcache, pdo_mysql, sodium, xml, zip

FROM php:8-fpm AS base
LABEL maintainer="ChurchCRM"

EXPOSE 9000

# Install system dependencies required by PHP extensions
RUN apt-get update && \
apt-get install -y \
libcurl4-openssl-dev \
libfreetype6-dev \
libicu-dev \
libjpeg-dev \
libonig-dev \
libpng-dev \
libxml2-dev \
libzip-dev \
gettext \
locales \
locales-all \
unzip \
&& rm -rf /var/lib/apt/lists/*

# Install and configure PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
docker-php-ext-install -j$(nproc) \
bcmath \
curl \
exif \
gd \
gettext \
iconv \
intl \
mbstring \
mysqli \
opcache \
pdo_mysql \
xml \
zip

# Sodium is bundled with PHP 7.2+ but needs to be enabled
RUN docker-php-ext-enable sodium

Comment on lines +49 to +54
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker-php-ext-enable sodium can fail on official PHP images when sodium is compiled in (no sodium.so to enable). To make the build reliable, either explicitly install it as a shared extension (docker-php-ext-install sodium) or drop this line if the base image already provides it enabled.

Suggested change
xml \
zip
# Sodium is bundled with PHP 7.2+ but needs to be enabled
RUN docker-php-ext-enable sodium
sodium \
xml \
zip

Copilot uses AI. Check for mistakes.
# Configure PHP settings
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" && \
sed -i 's/^upload_max_filesize.*$/upload_max_filesize = 2G/g' $PHP_INI_DIR/php.ini && \
sed -i 's/^post_max_size.*$/post_max_size = 2G/g' $PHP_INI_DIR/php.ini && \
sed -i 's/^memory_limit.*$/memory_limit = 256M/g' $PHP_INI_DIR/php.ini && \
sed -i 's/^max_execution_time.*$/max_execution_time = 120/g' $PHP_INI_DIR/php.ini


# Production stage - minimal runtime
FROM base AS prod


# Dev stage - includes build tools for local development inside the container
FROM base AS dev

# Install development tools
RUN apt-get update && \
apt-get install -y \
git \
make \
python3 \
unzip \
curl \
&& rm -rf /var/lib/apt/lists/*

# Install Xdebug for step-debugging
RUN pecl install xdebug && docker-php-ext-enable xdebug

# Install Composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
rm composer-setup.php

# Install NVM + Node.js (needed to run npm run deploy inside the container)
RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh -o /opt/node-install.sh && \
Comment on lines +83 to +89
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dev stage downloads and executes remote installer scripts (Composer + NVM) without integrity verification. Composer recommends verifying the installer signature; similarly, consider pinning to a commit SHA and/or verifying a checksum/signature for the NVM install script to reduce supply-chain risk.

Suggested change
# Install Composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
rm composer-setup.php
# Install NVM + Node.js (needed to run npm run deploy inside the container)
RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh -o /opt/node-install.sh && \
# Install Composer (with installer signature verification)
RUN EXPECTED_SIGNATURE="$(curl -s https://composer.github.io/installer.sig)" && \
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
ACTUAL_SIGNATURE="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" && \
if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]; then >&2 echo 'ERROR: Invalid Composer installer signature'; rm composer-setup.php; exit 1; fi && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
rm composer-setup.php
# Install NVM + Node.js (needed to run npm run deploy inside the container)
RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/b3f16ba1f5a1ab3d6a0eef8949b0f9b867951376/install.sh -o /opt/node-install.sh && \

Copilot uses AI. Check for mistakes.
chmod a+x /opt/node-install.sh && \
/opt/node-install.sh && \
rm /opt/node-install.sh

RUN /bin/bash -c "source /root/.nvm/nvm.sh && nvm install 24 && npm install -g node-gyp"
86 changes: 84 additions & 2 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Getting Started with Docker for Development
Getting Started with Docker for ChurchCRM
===========================

This directory contains two types of Docker configurations:

| Configuration | Use Case |
|---------------|----------|
| `docker-compose.yaml` | Development and testing (Apache + PHP). Used by the automated test suite and local development. |
| `docker-compose.nginx.yaml` | Self-hosted or production reference (nginx + PHP-FPM). Starting point for your own deployment. |
Comment on lines +6 to +9
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These markdown tables use double leading pipes (|| ... | ... |), which GitHub Markdown renders as extra empty columns. Use standard table syntax with single leading/trailing pipes (e.g., | Configuration | Use Case |) throughout the README (including the URL prefix table).

Copilot uses AI. Check for mistakes.

---

Development & Testing (Apache)
---

** THIS DOCKER CONFIGURATION IS INTENDED FOR DEVELOPMENT & TESTING PURPOSES ONLY**

ChurchCRM uses a single `docker-compose.yaml` with profiles to support different environments:
Expand Down Expand Up @@ -117,4 +129,74 @@ WEBSERVER_PORT=80
ADMINER_PORT=8088
MAILSERVER_PORT=1025
MAILSERVER_GUI_PORT=8025
```
```

---

Self-Hosted / Production (nginx + PHP-FPM)
---

The `docker-compose.nginx.yaml` and `nginx/default.conf` files provide a reference
configuration for deploying ChurchCRM with **nginx + PHP-FPM** instead of Apache.
This is the setup most commonly used in self-hosted environments (reverse-proxy
stacks, Kubernetes, etc.).

### Why nginx needs explicit routing

ChurchCRM is structured as multiple independent **Slim 4 PHP applications**, each
in its own subdirectory with its own `index.php` entry point:

| URL prefix | Entry point |
|------------|-------------|
| `/session/` | `session/index.php` — login, logout, 2FA |
| `/api/` | `api/index.php` — REST API |
| `/v2/` | `v2/index.php` — modern MVC pages |
| `/admin/` | `admin/index.php` — admin panel |
| `/finance/` | `finance/index.php` — finance module |
| `/kiosk/` | `kiosk/index.php` — check-in kiosk |
| `/plugins/` | `plugins/index.php` — plugin system |
| `/external/` | `external/index.php` — public integrations |
| `/setup/` | `setup/index.php` — first-run wizard |
| `/` | `index.php` — legacy PHP pages |

With **Apache**, each subdirectory's `.htaccess` file automatically routes
requests to the correct entry point.

With **nginx**, you must explicitly map each URL prefix to its entry point.
**Routing all requests to the root `index.php`** (a common mistake) causes an
infinite redirect loop because unauthenticated users are redirected to
`/session/begin`, but that path also goes to `index.php`, which redirects again.

### Quick start

```bash
# From the docker/ directory:
docker compose -f docker-compose.nginx.yaml up -d
```

Visit `http://localhost/` — you will see the setup wizard on first run.

### Files

| File | Description |
|------|-------------|
| `docker-compose.nginx.yaml` | Example Compose file (nginx + PHP-FPM + MariaDB) |
| `nginx/default.conf` | nginx server block with correct per-subdirectory routing |
| `Dockerfile.churchcrm-fpm-php8` | PHP-FPM image with all required extensions |

### Customising the nginx config

1. Copy `nginx/default.conf` to your deployment.
2. Replace `php-fpm:9000` with your PHP-FPM container hostname/port.
3. Set `root` to the path where ChurchCRM's `src/` contents are served from.
4. For a **subdirectory install** (e.g. `http://example.com/churchcrm/`):
- Set `$sRootPath = '/churchcrm'` in `Include/Config.php`.
- Prefix all `location` paths in the nginx config with `/churchcrm`.
- See the commented example at the bottom of `nginx/default.conf`.

### Required PHP extensions

`bcmath`, `curl`, `exif`, `gd`, `gettext`, `iconv`, `intl`, `mbstring`, `mysqli`,
`opcache`, `pdo_mysql`, `sodium`, `xml`, `zip`

The `Dockerfile.churchcrm-fpm-php8` installs all of these.
75 changes: 75 additions & 0 deletions docker/docker-compose.nginx.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# docker-compose.nginx.yaml — Example nginx + PHP-FPM deployment for ChurchCRM
#
# This is a REFERENCE CONFIGURATION for self-hosted deployments using nginx +
# PHP-FPM instead of Apache. It is NOT used by the automated test suite (which
# uses docker-compose.yaml with Apache). Use this as a starting point for your
# own production or staging environment.
#
# QUICK START
# -----------
# 1. Copy this file and docker/nginx/default.conf to your deployment directory.
# 2. Copy docker/Config.php to Include/Config.php in your ChurchCRM source tree
# and set the database connection details to match the values below.
# 3. Set MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD to strong secrets.
# 4. Run: docker compose -f docker-compose.nginx.yaml up -d
# 5. Visit http://localhost/ - you will be redirected to the setup wizard on
# first run (no Config.php) or to the login page after installation.
#
# IMPORTANT: Change all passwords before exposing this to the internet.

services:

database:
image: mariadb:11
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-changeme_root}
MYSQL_DATABASE: churchcrm
MYSQL_USER: churchcrm
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-changeme}
volumes:
- churchcrm-db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
timeout: 20s
retries: 10
hostname: database

php-fpm:
build:
context: .
dockerfile: Dockerfile.churchcrm-fpm-php8
target: prod
image: churchcrm/crm:php8-fpm
restart: unless-stopped
volumes:
# Mount ChurchCRM source (the contents of src/) here.
# Options:
# a) Replace with a bind mount to a local directory:
# - /path/to/churchcrm/src:/var/www/html
# b) Copy files into this named volume after the first `docker compose up`:
# docker cp /path/to/churchcrm/src/. churchcrm-php-fpm-1:/var/www/html/
# c) Build a custom image with COPY instructions in your own Dockerfile.
- churchcrm-www:/var/www/html
depends_on:
database:
condition: service_healthy
hostname: php-fpm

webserver:
image: nginx:stable-alpine
restart: unless-stopped
ports:
- "${WEBSERVER_PORT:-80}:80"
volumes:
# Same source volume as php-fpm so nginx can serve static files directly
- churchcrm-www:/var/www/html:ro
# Mount the nginx configuration
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php-fpm
hostname: webserver

volumes:
churchcrm-db:
churchcrm-www:
Loading
Loading