]> git.ekhem.eu.org Git - guix.git/commitdiff
[vps] Add nginx web server on port 80.
authorJakub Czajka <jakub@ekhem.eu.org>
Mon, 20 Jul 2026 22:14:56 +0000 (22:14 +0000)
committerJakub Czajka <jakub@ekhem.eu.org>
Thu, 30 Jul 2026 16:01:40 +0000 (16:01 +0000)
Co-Authored-By: Claude <noreply@anthropic.com>
conf/vps/nginx.conf [new file with mode: 0644]
conf/vps/nginx.scm [new file with mode: 0644]
tests/nginx.scm [new file with mode: 0644]
vps-system.scm

diff --git a/conf/vps/nginx.conf b/conf/vps/nginx.conf
new file mode 100644 (file)
index 0000000..63255e7
--- /dev/null
@@ -0,0 +1,35 @@
+user nginx nginx;
+pid /var/run/nginx/pid;
+error_log /var/log/nginx/error.log error;
+
+events {
+    worker_connections 1024;
+}
+
+http {
+    include @MIME_TYPES@;
+    default_type application/octet-stream;
+
+    sendfile on;
+    tcp_nopush on;
+    keepalive_timeout 65;
+    server_tokens off;
+
+    gzip on;
+    gzip_vary on;
+    gzip_proxied any;
+    gzip_comp_level 6;
+    gzip_types text/plain text/css text/xml
+               application/json application/javascript
+               application/xml+rss image/svg+xml;
+
+    client_body_temp_path /var/run/nginx/client_body_temp;
+    proxy_temp_path /var/run/nginx/proxy_temp;
+    fastcgi_temp_path /var/run/nginx/fastcgi_temp;
+    uwsgi_temp_path /var/run/nginx/uwsgi_temp;
+    scgi_temp_path /var/run/nginx/scgi_temp;
+
+    access_log /var/log/nginx/access.log combined;
+
+    include @SITES_DIR@/*.conf;
+}
diff --git a/conf/vps/nginx.scm b/conf/vps/nginx.scm
new file mode 100644 (file)
index 0000000..eeb8211
--- /dev/null
@@ -0,0 +1,117 @@
+;;; Copyright (c) 2026 Jakub Czajka <jakub@ekhem.eu.org>
+;;; License: GPL-3.0 or later.
+;;;
+;;; nginx.scm — Nginx server configuration, with site configs
+;;; collected from all site package inputs under
+;;; etc/nginx/sites-available/.
+
+(define-module (conf vps nginx)
+  #:use-module (conf vps website)
+  #:use-module (gnu packages tls)
+  #:use-module (gnu packages web)
+  #:use-module (gnu services)
+  #:use-module (gnu services web)
+  #:use-module (guix packages)
+  #:use-module (guix build-system trivial)
+  #:use-module (guix gexp)
+  #:use-module ((guix licenses)
+                #:prefix license:)
+  #:use-module (ice-9 ftw)
+  #:use-module (srfi srfi-1)
+  #:export (nginx-config %nginx-service))
+
+(define nginx-config-file
+  (local-file (canonicalize-path (string-append (getenv "GUIX_PACKAGE_PATH")
+                                                "/conf/vps/nginx.conf"))
+              "nginx.conf"))
+
+(define nginx-config
+  (package
+    (name "nginx-config")
+    (version "20260725")
+    (source
+     nginx-config-file)
+    (build-system trivial-build-system)
+    (arguments
+     (list
+      #:modules '((guix build utils)
+                  (ice-9 ftw)
+                  (srfi srfi-1))
+      #:builder
+      #~(begin
+          (use-modules (guix build utils)
+                       (ice-9 ftw)
+                       (srfi srfi-1))
+          (let* ((out #$output)
+                 (etc (string-append out "/etc/nginx"))
+                 (sites (string-append etc "/sites-available")))
+            (mkdir-p sites)
+            ;; 1. Copy and patch the main nginx.conf.
+            (copy-file #$source
+                       (string-append etc "/nginx.conf"))
+            (substitute* (string-append etc "/nginx.conf")
+              (("@MIME_TYPES@")
+               #$(file-append nginx "/share/nginx/conf/mime.types"))
+              (("@SITES_DIR@")
+               sites))
+            ;; 1.5.  Generate a self-signed certificate so that
+            ;; nginx -t passes even without real SSL certs.
+            (let* ((openssl-bin #$(file-append openssl "/bin/openssl"))
+                   (certs (string-append out "/etc/nginx/certs")))
+              (mkdir-p certs)
+              (invoke openssl-bin
+                      "req"
+                      "-x509"
+                      "-newkey"
+                      "rsa:2048"
+                      "-keyout"
+                      (string-append certs "/privkey.pem")
+                      "-out"
+                      (string-append certs "/fullchain.pem")
+                      "-days"
+                      "365"
+                      "-nodes"
+                      "-subj"
+                      "/CN=example.org"))
+            ;; 2. Symlink site configs from all inputs.
+            (define (site-configs dir)
+              "Return the list of .conf files in DIR."
+              (filter (lambda (f)
+                        (string-suffix? ".conf" f))
+                      (or (scandir dir)
+                          '())))
+            (define (collect input)
+              "Symlink site configs from INPUT into sites/."
+              (let ((dir (string-append (cdr input)
+                                        "/etc/nginx/sites-available")))
+                (when (directory-exists? dir)
+                  (unless (member (car input)
+                                  '("source" "nginx"))
+                    (for-each (lambda (f)
+                                (symlink (string-append dir "/" f)
+                                         (string-append sites "/" f)))
+                              (site-configs dir))))))
+            (for-each collect %build-inputs)
+            ;; 3.  Substitute the SSL cert directory in
+            ;; site configs so nginx -t passes.
+            (let ((certs (string-append out "/etc/nginx/certs")))
+              (for-each (lambda (f)
+                          (substitute* (string-append sites "/" f)
+                            (("\\$\\{public_ssl_cert_dir\\}")
+                             certs)))
+                        (site-configs sites)))))))
+    (inputs (list nginx openssl website))
+    (home-page "https://git.ekhem.eu.org")
+    (synopsis "Nginx configuration for the personal VPS")
+    (description "Nginx configuration bundling @file{nginx.conf} and
+site-specific configurations auto-collected from all
+site package inputs under
+@file{etc/nginx/sites-available/}.")
+    (license license:gpl3+)))
+
+(define %nginx-config
+  (let ((conf (file-append nginx-config "/etc/nginx/nginx.conf")))
+    (nginx-configuration (file conf))))
+
+(define %nginx-service
+  (service nginx-service-type %nginx-config))
diff --git a/tests/nginx.scm b/tests/nginx.scm
new file mode 100644 (file)
index 0000000..c7ea83f
--- /dev/null
@@ -0,0 +1,32 @@
+;;; Copyright (c) 2026 Jakub Czajka <jakub@ekhem.eu.org>
+;;; License: GPL-3.0 or later.
+;;;
+;;; nginx test cases.
+
+(define-module (tests nginx)
+  #:use-module (conf vps website)
+  #:use-module (gnu packages web)
+  #:use-module (guix gexp)
+  #:export (nginx-test-cases))
+
+(define %nginx-binary
+  (file-append nginx "/sbin/nginx"))
+
+(define (nginx-test-cases marionette)
+  "Return a gexp with nginx test assertions."
+  #~(begin
+      (test-assert "nginx: service running"
+                   (marionette-eval
+                    '(begin (use-modules (gnu services herd))
+                            (start-service 'nginx))
+                    #$marionette))
+
+      (test-assert "nginx: port 80 TCP"
+                   (wait-for-tcp-port 80 #$marionette))
+
+      (test-assert "nginx: config syntax is valid"
+                   (marionette-eval
+                    '(zero? (system* #$%nginx-binary
+                                     "-c" "/etc/nginx/nginx.conf"
+                                     "-t"))
+                    #$marionette))))
index 2258573d03843c26623c1ad3793abb5d5c7be57a..30d69ddfbfd9747415870b2322d0240b45959ae8 100644 (file)
@@ -13,6 +13,7 @@
   #:use-module (nongnu packages linux)
   #:use-module (conf vps sshd)
   #:use-module (conf vps dnscrypt)
+  #:use-module (conf vps nginx)
   #:export (vps-operating-system))
 
 (use-service-modules networking shepherd)
@@ -81,6 +82,8 @@
               ;; etc-service-type rather than being one.
               (service resolv-conf-service-type)
 
+              %nginx-service
+
               ;; TODO: nginx stream for DNS-over-TLS on port 853.
               ;; See server.git dnscrypt-proxy/dnscrypt-proxy.conf
               ;; and the metadata.git Ansible playbook for the