From 3ace2e661b8b6d70cf92f94d6f239c24af0bb08c Mon Sep 17 00:00:00 2001 From: Philip Peterson <1326208+philip-peterson@users.noreply.github.com> Date: Thu, 25 Jun 2026 21:43:10 -0700 Subject: [PATCH] add wordpress --- nixos/arion-atitraining/arion-compose.nix | 56 +++++++++++++++++++++++ nixos/arion-atitraining/arion-pkgs.nix | 3 ++ nixos/atitraining/Dockerfile | 37 +++++++++++++++ nixos/atitraining/entrypoint.sh | 6 +++ nixos/atitraining/wp-init-bg.sh | 25 ++++++++++ nixos/linux.nix | 26 +++++++++++ nixos/nginx.nix | 15 ++++++ 7 files changed, 168 insertions(+) create mode 100644 nixos/arion-atitraining/arion-compose.nix create mode 100644 nixos/arion-atitraining/arion-pkgs.nix create mode 100644 nixos/atitraining/Dockerfile create mode 100644 nixos/atitraining/entrypoint.sh create mode 100644 nixos/atitraining/wp-init-bg.sh diff --git a/nixos/arion-atitraining/arion-compose.nix b/nixos/arion-atitraining/arion-compose.nix new file mode 100644 index 0000000..024792d --- /dev/null +++ b/nixos/arion-atitraining/arion-compose.nix @@ -0,0 +1,56 @@ +{ pkgs, ... }: +{ + project.name = "atitraining"; + + networks.atitraining.external = false; + + services = { + db = { + service = { + image = "mariadb:11.4"; + container_name = "atitraining-db"; + restart = "unless-stopped"; + networks = [ "atitraining" ]; + volumes = [ + "/var/atitraining/db:/var/lib/mysql" + ]; + environment = { + MARIADB_DATABASE = "atitraining"; + MARIADB_USER = "atitraining"; + MARIADB_PASSWORD = "atitraining"; + MARIADB_ROOT_PASSWORD = "atitrainingroot"; + }; + healthcheck = { + test = [ "CMD" "healthcheck.sh" "--connect" "--innodb_initialized" ]; + interval = "10s"; + timeout = "5s"; + retries = 10; + }; + }; + }; + + wordpress = { + service = { + # Image pre-built by build-atitraining-image.service from nixos/atitraining/Dockerfile + # Pinned to WordPress 6.7.2-php8.3-apache. + image = "atitraining-wordpress:local"; + container_name = "atitraining-wordpress"; + restart = "unless-stopped"; + networks = [ "atitraining" ]; + depends_on = [ "db" ]; + ports = [ "127.0.0.1:3015:80" ]; + volumes = [ + "/var/atitraining/wp-uploads:/var/www/html/wp-content/uploads" + ]; + environment = { + WORDPRESS_DB_HOST = "db"; + WORDPRESS_DB_NAME = "atitraining"; + WORDPRESS_DB_USER = "atitraining"; + WORDPRESS_DB_PASSWORD = "atitraining"; + # Change this after first login via /wp-admin + WORDPRESS_ADMIN_PASSWORD = "changeme"; + }; + }; + }; + }; +} diff --git a/nixos/arion-atitraining/arion-pkgs.nix b/nixos/arion-atitraining/arion-pkgs.nix new file mode 100644 index 0000000..1d16305 --- /dev/null +++ b/nixos/arion-atitraining/arion-pkgs.nix @@ -0,0 +1,3 @@ +import { + system = "x86_64-linux"; +} diff --git a/nixos/atitraining/Dockerfile b/nixos/atitraining/Dockerfile new file mode 100644 index 0000000..28773a3 --- /dev/null +++ b/nixos/atitraining/Dockerfile @@ -0,0 +1,37 @@ +FROM wordpress:6.7.2-php8.3-apache + +# Install WP-CLI and dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + less \ + default-mysql-client \ + unzip \ + && curl -sL -o /usr/local/bin/wp \ + https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ + && chmod +x /usr/local/bin/wp \ + && rm -rf /var/lib/apt/lists/* + +# Pre-install the p2 theme. +# p2 turns WordPress into a real-time microblog stream (Twitter-like) — +# intentionally weird for a traditional blog context. +RUN curl -sL https://downloads.wordpress.org/theme/p2.zip -o /tmp/p2.zip \ + && unzip -q /tmp/p2.zip -d /var/www/html/wp-content/themes/ \ + && rm /tmp/p2.zip \ + && chown -R www-data:www-data /var/www/html/wp-content/themes/p2 + +# Tell WordPress it's behind an HTTPS reverse proxy +ENV WORDPRESS_CONFIG_EXTRA="if (isset(\$_SERVER['HTTP_X_FORWARDED_PROTO']) && \$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { \$_SERVER['HTTPS'] = 'on'; }" + +# Blog identity — baked into the image, overridable at runtime +ENV WORDPRESS_BLOG_TITLE="ATI Training" +ENV WORDPRESS_BLOG_TAGLINE="advanced training intelligence" +ENV WORDPRESS_ADMIN_USER="admin" +ENV WORDPRESS_ADMIN_EMAIL="peterson@sent.com" +ENV WORDPRESS_ACTIVE_THEME="p2" +ENV WORDPRESS_SITEURL="https://atitraining.coldairnetworks.com" + +COPY wp-init-bg.sh /usr/local/bin/wp-init-bg.sh +COPY entrypoint.sh /usr/local/bin/atitraining-entrypoint.sh +RUN chmod +x /usr/local/bin/wp-init-bg.sh /usr/local/bin/atitraining-entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/atitraining-entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/nixos/atitraining/entrypoint.sh b/nixos/atitraining/entrypoint.sh new file mode 100644 index 0000000..7d1341b --- /dev/null +++ b/nixos/atitraining/entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +# Run WP-CLI setup in background; it waits for wp-config.php and the DB +/usr/local/bin/wp-init-bg.sh & +# Hand off to the official WordPress entrypoint (writes wp-config.php, starts Apache) +exec /usr/local/bin/docker-entrypoint.sh "$@" diff --git a/nixos/atitraining/wp-init-bg.sh b/nixos/atitraining/wp-init-bg.sh new file mode 100644 index 0000000..1f997d9 --- /dev/null +++ b/nixos/atitraining/wp-init-bg.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +# Wait for the official WP entrypoint to write wp-config.php +until [ -f /var/www/html/wp-config.php ]; do sleep 1; done + +# Wait for the database to accept connections +until wp --allow-root --path=/var/www/html db check 2>/dev/null; do + sleep 3 +done + +# Perform first-time install only if WordPress isn't installed yet +if ! wp --allow-root --path=/var/www/html core is-installed 2>/dev/null; then + wp --allow-root --path=/var/www/html core install \ + --url="${WORDPRESS_SITEURL:-https://atitraining.coldairnetworks.com}" \ + --title="${WORDPRESS_BLOG_TITLE:-ATI Training}" \ + --admin_user="${WORDPRESS_ADMIN_USER:-admin}" \ + --admin_password="${WORDPRESS_ADMIN_PASSWORD:-changeme}" \ + --admin_email="${WORDPRESS_ADMIN_EMAIL:-peterson@sent.com}" \ + --skip-email + wp --allow-root --path=/var/www/html theme activate \ + "${WORDPRESS_ACTIVE_THEME:-p2}" + wp --allow-root --path=/var/www/html option update blogdescription \ + "${WORDPRESS_BLOG_TAGLINE:-advanced training intelligence}" +fi diff --git a/nixos/linux.nix b/nixos/linux.nix index e0572eb..a3f993a 100644 --- a/nixos/linux.nix +++ b/nixos/linux.nix @@ -31,6 +31,8 @@ pullomatic = "${pullomaticPkg}/bin/pullomatic"; + atitrainingContext = builtins.path { path = ./atitraining; name = "atitraining"; }; + in { imports = [ (import ./cloned_repos {inherit pkgs pullomatic lib;}) @@ -189,6 +191,7 @@ in { projects.pluto.settings = import ./arion-pluto/arion-compose.nix; projects.paperless.settings = import ./arion-paperless/arion-compose.nix; projects.openclaw.settings = import ./arion-openclaw/arion-compose.nix; + projects.atitraining.settings = import ./arion-atitraining/arion-compose.nix; }; # The arion NixOS module sets backend = "podman-socket" but doesn't inject @@ -199,6 +202,26 @@ in { systemd.services.arion-pluto.environment.DOCKER_HOST = "unix:///run/podman/podman.sock"; systemd.services.arion-paperless.environment.DOCKER_HOST = "unix:///run/podman/podman.sock"; systemd.services.arion-openclaw.environment.DOCKER_HOST = "unix:///run/podman/podman.sock"; + systemd.services.arion-atitraining.environment.DOCKER_HOST = "unix:///run/podman/podman.sock"; + + # Build the WordPress image for atitraining before arion starts it. + # restartTriggers ensures the image rebuilds whenever nixos/atitraining/ changes. + systemd.services.build-atitraining-image = { + description = "Build atitraining WordPress image"; + wantedBy = [ "arion-atitraining.service" ]; + before = [ "arion-atitraining.service" ]; + restartTriggers = [ atitrainingContext ]; + path = [ pkgs.podman ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + podman build -t atitraining-wordpress:local ${atitrainingContext} + ''; + }; + systemd.services.arion-atitraining.after = lib.mkAfter [ "build-atitraining-image.service" ]; + systemd.services.arion-atitraining.wants = [ "build-atitraining-image.service" ]; systemd.services.novnc = { description = "noVNC WebSocket proxy for VNC desktop"; @@ -294,6 +317,8 @@ in { "d /var/coldairnetworks-db/postgres 0755 root root" "d /var/coldairnetworks-db/pgadmin 0700 5050 5050" "d /var/coldairnetworks-db/ssl 0755 root root" + "d /var/atitraining/db 0755 root root" + "d /var/atitraining/wp-uploads 0755 root root" ]; networking.hostName = "${hostname}"; @@ -534,5 +559,6 @@ in { "acme-selfsigned-pluto.philippeterson.com.service" "acme-selfsigned-paperless.philippeterson.com.service" "acme-selfsigned-db.coldairnetworks.com.service" + "acme-selfsigned-atitraining.coldairnetworks.com.service" ]; } diff --git a/nixos/nginx.nix b/nixos/nginx.nix index ac2b5a9..464493c 100644 --- a/nixos/nginx.nix +++ b/nixos/nginx.nix @@ -90,6 +90,21 @@ }; }; + "atitraining.coldairnetworks.com" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:3015/"; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + }; + "db.coldairnetworks.com" = { enableACME = true; forceSSL = true;