Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c0582a0
feat(smarthost): add discover-smarthost script for SMTP configuration
stephdl Feb 9, 2026
fb735d1
fix(container): add openssl installation to Containerfile
stephdl Feb 9, 2026
d16c662
feat(service): add smarthost environment file and SMTP environment va…
stephdl Feb 9, 2026
6205660
feat(smarthost): add script to reload lamp service on smarthost changes
stephdl Feb 9, 2026
dd690cf
refactor(build-images): streamline PHP FPM image builds with a dedica…
stephdl Feb 9, 2026
0915f2b
Update copyright year in discover-smarthost
stephdl Feb 10, 2026
2f522a3
Update copyright year in 10reload_services
stephdl Feb 10, 2026
4def4d5
Add runagent discover-smarthost to ExecStartPre
stephdl Feb 10, 2026
7b31f6c
fix(smarthost): update service name to webserver in reload script
stephdl Feb 10, 2026
232c0b2
docs(smarthost): add discovery settings and environment file details …
stephdl Feb 10, 2026
8d5e840
Fix typo in README regarding webserver
stephdl Feb 10, 2026
109370d
fix(discover-smarthost): update envfile naming to include PHP value a…
stephdl Feb 16, 2026
442a9c4
feat(smarthost): add script to restart PHP-FPM services based on conf…
stephdl Feb 16, 2026
43dfbd9
fix(smarthost): correct environment file reference and update discove…
stephdl Feb 16, 2026
93914a1
fix(smarthost): update environment file paths to include 'smarthosts'…
stephdl Feb 16, 2026
a16d9dc
Apply suggestions from code review
stephdl Feb 16, 2026
6232e3a
fix(smarthost): simplify environment file handling and remove PHP arg…
stephdl Feb 16, 2026
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
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,42 @@ Warning: using user "cluster" credentials from the environment
"http2https": true
}
```
## Smarthost setting discovery

Some configuration settings, like the smarthost setup, are not part of the
`configure-module` action input: they are discovered by looking at some
Redis keys. To ensure the module is always up-to-date with the
centralized [smarthost
setup](https://nethserver.github.io/ns8-core/core/smarthost/) every time
`phpfpm@.service` starts, the command `bin/discover-smarthost` runs and refreshes
the `state/smarthost.env` file with fresh values from Redis.

Furthermore if smarthost setup is changed when webserver is already
running, the event handler `events/smarthost-changed/10reload_services`
Comment on lines +356 to +360
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

Documentation doesn’t match the implementation: the unit references state/smarthosts/smarthost.env (and discover-smarthost writes under smarthosts/), but the README mentions state/smarthost.env. Also, the documented handler name events/smarthost-changed/10reload_services doesn’t exist in this PR (added file is 10restart_phpfpm). Update the paths/names to reflect the actual files.

Copilot uses AI. Check for mistakes.
restarts `phpfpm@.service`.

See also the `systemd/user/phpfpm@.service` file.

### Environment files for PHP versions

The systemd service is templated using `phpfpm@.service` to support multiple PHP versions.
Environment files are present and loaded inside the container for each PHP version instance.
For example, when the systemd service `phpfpm@8.5.service` runs, it loads the following environment files:

- `state/smarthost.env` - SMTP and smarthost configuration (optional, prefixed with `SMTP_`)

These environment variables are automatically passed to the corresponding PHP-FPM container instance, allowing configuration to be centrally managed through environment files that apply to all PHP versions (8.5, 8.4, 8.3, etc.).
This setting discovery is just an example to understand how the module is expected to work:
```
cat smarthost.env
Comment on lines +371 to +376
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

This list item and the example cat smarthost.env command don’t reflect where the file is actually created/loaded (the unit uses state/smarthosts/smarthost.env). Update the bullet and example command/path to match the real location.

Copilot uses AI. Check for mistakes.
SMTP_ENABLED=1
SMTP_HOST=10.5.4.1
SMTP_PORT=25
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_ENCRYPTION=none
SMTP_TLSVERIFY=
```

## Uninstall

Expand Down
85 changes: 23 additions & 62 deletions build-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,68 +10,29 @@ repobase="${REPOBASE:-ghcr.io/nethserver}"
# Configure the image name
reponame="webserver"

podman build \
--force-rm \
--layers \
--tag "${repobase}/php8.5-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:8.5.1-fpm-bookworm" \
container

images+=("${repobase}/php8.5-fpm")

podman build \
--force-rm \
--layers \
--tag "${repobase}/php8.4-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:8.4.15-fpm-bookworm" \
container

images+=("${repobase}/php8.4-fpm")

podman build \
--force-rm \
--layers \
--tag "${repobase}/php8.3-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:8.3.28-fpm-bookworm" \
container

images+=("${repobase}/php8.3-fpm")

podman build \
--force-rm \
--layers \
--tag "${repobase}/php8.2-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:8.2.29-fpm-bookworm" \
container

images+=("${repobase}/php8.2-fpm")

podman build \
--force-rm \
--layers \
--tag "${repobase}/php8.1-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:8.1.33-fpm-bookworm" \
container

images+=("${repobase}/php8.1-fpm")

podman build \
--force-rm \
--layers \
--tag "${repobase}/php8.0-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:8.0.30-fpm-bullseye" \
container

images+=("${repobase}/php8.0-fpm")

podman build \
--force-rm \
--layers \
--tag "${repobase}/php7.4-fpm" \
--build-arg "PHP_VERSION_IMAGE=docker.io/library/php:7.4.33-fpm-bullseye" \
container

images+=("${repobase}/php7.4-fpm")
# Function to build PHP FPM images
build_php_image() {
local version=$1
local php_image=$2

podman build \
--force-rm \
--layers \
--tag "${repobase}/php${version}-fpm" \
--build-arg "PHP_VERSION_IMAGE=${php_image}" \
container

images+=("${repobase}/php${version}-fpm")
}

# Build all PHP FPM images
build_php_image "8.5" "docker.io/library/php:8.5.1-fpm-bookworm"
build_php_image "8.4" "docker.io/library/php:8.4.15-fpm-bookworm"
build_php_image "8.3" "docker.io/library/php:8.3.28-fpm-bookworm"
build_php_image "8.2" "docker.io/library/php:8.2.29-fpm-bookworm"
build_php_image "8.1" "docker.io/library/php:8.1.33-fpm-bookworm"
build_php_image "8.0" "docker.io/library/php:8.0.30-fpm-bullseye"
build_php_image "7.4" "docker.io/library/php:7.4.33-fpm-bullseye"

# Create a new empty container image
container=$(buildah from scratch)
Expand Down
2 changes: 1 addition & 1 deletion container/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ RUN set -eux \
# --- Install runtime libraries depending on Debian codename ---
&& apt update \
&& apt upgrade -y \
&& apt install -y ca-certificates \
&& apt install -y ca-certificates openssl\
&& if echo "$PHP_VERSION_IMAGE" | grep -q "bullseye$"; then \
apt install -y --no-install-recommends \
libc-client2007e \
Expand Down
36 changes: 36 additions & 0 deletions imageroot/bin/discover-smarthost
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3

#
# Copyright (C) 2026 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import agent
import os

# Connect the local Redis replica. This is necessary to consistently start
# the service if the leader node is not reachable:
rdb = agent.redis_connect(use_replica=True)
smtp_settings = agent.get_smarthost_settings(rdb)

# create the smarthost folder
envfile = 'smarthosts/smarthost.env'

# Ensure the target directory exists:
os.makedirs(os.path.dirname(envfile), exist_ok=True)

# Create a unique temporary file using process PID to avoid concurrent writes:
tmp_path = f"{envfile}.tmp.{os.getpid()}"

with open(tmp_path, "w") as efp:
# HINT for lamp: adjust variable names as needed
print(f"SMTP_ENABLED={'1' if smtp_settings['enabled'] else ''}", file=efp)
print(f"SMTP_HOST={smtp_settings['host']}", file=efp)
print(f"SMTP_PORT={smtp_settings['port']}", file=efp)
print(f"SMTP_USERNAME={smtp_settings['username']}", file=efp)
print(f"SMTP_PASSWORD={smtp_settings['password']}", file=efp)
print(f"SMTP_ENCRYPTION={smtp_settings['encrypt_smtp']}", file=efp)
print(f"SMTP_TLSVERIFY={'1' if smtp_settings['tls_verify'] else ''}", file=efp)

# Commit changes by atomically replacing the existing envfile:
os.replace(tmp_path, envfile)
19 changes: 19 additions & 0 deletions imageroot/events/smarthost-changed/10restart_phpfpm
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3

#
# Copyright (C) 2026 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import subprocess
import glob
import re

# detect if a service has configurations and restart it
PhpServiceArray = glob.glob('php*-fpm-custom.d')
for folder in PhpServiceArray:
ConfiguredServices = re.findall('php[0-9\.]+', folder)
ListConfigurations = glob.glob(folder+'/dyn-*.conf')
if ListConfigurations :
subprocess.run(["download-php-fpm",ConfiguredServices[0].replace('php','')])
subprocess.run(["systemctl", "--user", "restart", "phpfpm@"+ConfiguredServices[0].replace('php','')+".service"])
Comment on lines +16 to +19
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

This handler assumes re.findall(...) always returns at least one match and then indexes ConfiguredServices[0]; if the folder name format ever differs, this will raise IndexError. Also, subprocess.run(...) results are ignored, so a failed download/restart won’t be detected. Add a guard for empty matches and run subprocesses with error handling (e.g. check=True or inspecting returncode).

Suggested change
ListConfigurations = glob.glob(folder+'/dyn-*.conf')
if ListConfigurations :
subprocess.run(["download-php-fpm",ConfiguredServices[0].replace('php','')])
subprocess.run(["systemctl", "--user", "restart", "phpfpm@"+ConfiguredServices[0].replace('php','')+".service"])
if not ConfiguredServices:
# Skip folders that do not match the expected php version pattern
continue
php_version = ConfiguredServices[0].replace('php', '')
ListConfigurations = glob.glob(folder + '/dyn-*.conf')
if ListConfigurations:
try:
subprocess.run(["download-php-fpm", php_version], check=True)
subprocess.run(
["systemctl", "--user", "restart", f"phpfpm@{php_version}.service"],
check=True,
)
except subprocess.CalledProcessError as e:
print(f"Error handling php-fpm service for {php_version}: {e}", flush=True)

Copilot uses AI. Check for mistakes.
3 changes: 3 additions & 0 deletions imageroot/systemd/user/phpfpm@.service
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
EnvironmentFile=%S/state/environment
EnvironmentFile=%S/state/image_url_tag.env
WorkingDirectory=%S/state
EnvironmentFile=-%S/state/smarthosts/smarthost.env
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

EnvironmentFile is evaluated before any ExecStartPre commands run, so the file generated by ExecStartPre=...discover-smarthost will not be loaded into the unit environment for this start. To ensure fresh values are used, either pass the generated file directly to Podman (e.g., via --env-file), or generate/update the env file in a separate unit that runs before phpfpm@.service starts.

Copilot uses AI. Check for mistakes.
Restart=always
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/php%i-fpm.pid %t/php%i-fpm.ctr-id
ExecStartPre=/usr/bin/mkdir -p %S/state/php%i-fpm-custom.d
ExecStartPre=-runagent discover-smarthost
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

ExecStartPre=-runagent discover-smarthost uses runagent without an absolute path, while other units (e.g. nginx.service) use /usr/local/bin/runagent. In systemd units the PATH can be minimal, so this may fail with “runagent: not found”; use the same absolute path consistently.

Suggested change
ExecStartPre=-runagent discover-smarthost
ExecStartPre=-/usr/local/bin/runagent discover-smarthost

Copilot uses AI. Check for mistakes.
ExecStart=/usr/bin/podman run --conmon-pidfile %t/php%i-fpm.pid \
--cidfile %t/php%i-fpm.ctr-id --cgroups=no-conmon \
--pod-id-file %t/webserver.pod-id --replace -d --name php%i-fpm \
--volume websites:/var/www/html:z \
--volume %S/state/php%i-fpm-custom.d:/usr/local/etc/php-fpm.d:z \
--env SMTP_* \
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

podman run --env SMTP_* will pass a literal variable name SMTP_* (systemd does not do shell glob expansion for ExecStart arguments, and * is not a valid env var name). As a result, SMTP variables won’t be propagated into the container. Prefer --env-file pointing to the generated env file, or explicitly list each SMTP_... variable to forward.

Suggested change
--env SMTP_* \
--env-file %S/state/smarthosts/smarthost.env \

Copilot uses AI. Check for mistakes.
ghcr.io/nethserver/php%i-fpm:${IMAGE_URL_TAG}
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/php%i-fpm.ctr-id -t 10
ExecReload=/usr/bin/mkdir -p %S/state/php%i-fpm-custom.d
Expand Down
Loading