about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/web-servers/apache-httpd
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services/web-servers/apache-httpd')
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix750
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix78
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/limesurvey.nix196
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix349
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/mercurial.nix75
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix188
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/phabricator.nix50
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix103
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix121
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/wordpress.nix285
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix84
11 files changed, 2279 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
new file mode 100644
index 000000000000..8f00f81b078c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -0,0 +1,750 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  mainCfg = config.services.httpd;
+
+  httpd = mainCfg.package.out;
+
+  version24 = !versionOlder httpd.version "2.4";
+
+  httpdConf = mainCfg.configFile;
+
+  php = mainCfg.phpPackage.override { apacheHttpd = httpd.dev; /* otherwise it only gets .out */ };
+
+  phpMajorVersion = head (splitString "." php.version);
+
+  mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
+
+  defaultListen = cfg: if cfg.enableSSL
+    then [{ip = "*"; port = 443;}]
+    else [{ip = "*"; port = 80;}];
+
+  getListen = cfg:
+    let list = (lib.optional (cfg.port != 0) {ip = "*"; port = cfg.port;}) ++ cfg.listen;
+    in if list == []
+        then defaultListen cfg
+        else list;
+
+  listenToString = l: "${l.ip}:${toString l.port}";
+
+  extraModules = attrByPath ["extraModules"] [] mainCfg;
+  extraForeignModules = filter isAttrs extraModules;
+  extraApacheModules = filter isString extraModules;
+
+
+  makeServerInfo = cfg: {
+    # Canonical name must not include a trailing slash.
+    canonicalNames =
+      let defaultPort = (head (defaultListen cfg)).port; in
+      map (port:
+        (if cfg.enableSSL then "https" else "http") + "://" +
+        cfg.hostName +
+        (if port != defaultPort then ":${toString port}" else "")
+        ) (map (x: x.port) (getListen cfg));
+
+    # Admin address: inherit from the main server if not specified for
+    # a virtual host.
+    adminAddr = if cfg.adminAddr != null then cfg.adminAddr else mainCfg.adminAddr;
+
+    vhostConfig = cfg;
+    serverConfig = mainCfg;
+    fullConfig = config; # machine config
+  };
+
+
+  allHosts = [mainCfg] ++ mainCfg.virtualHosts;
+
+
+  callSubservices = serverInfo: defs:
+    let f = svc:
+      let
+        svcFunction =
+          if svc ? function then svc.function
+          # instead of using serviceType="mediawiki"; you can copy mediawiki.nix to any location outside nixpkgs, modify it at will, and use serviceExpression=./mediawiki.nix;
+          else if svc ? serviceExpression then import (toString svc.serviceExpression)
+          else import (toString "${toString ./.}/${if svc ? serviceType then svc.serviceType else svc.serviceName}.nix");
+        config = (evalModules
+          { modules = [ { options = res.options; config = svc.config or svc; } ];
+            check = false;
+          }).config;
+        defaults = {
+          extraConfig = "";
+          extraModules = [];
+          extraModulesPre = [];
+          extraPath = [];
+          extraServerPath = [];
+          globalEnvVars = [];
+          robotsEntries = "";
+          startupScript = "";
+          enablePHP = false;
+          enablePerl = false;
+          phpOptions = "";
+          options = {};
+          documentRoot = null;
+        };
+        res = defaults // svcFunction { inherit config lib pkgs serverInfo php; };
+      in res;
+    in map f defs;
+
+
+  # !!! callSubservices is expensive
+  subservicesFor = cfg: callSubservices (makeServerInfo cfg) cfg.extraSubservices;
+
+  mainSubservices = subservicesFor mainCfg;
+
+  allSubservices = mainSubservices ++ concatMap subservicesFor mainCfg.virtualHosts;
+
+
+  enableSSL = any (vhost: vhost.enableSSL) allHosts;
+
+
+  # Names of modules from ${httpd}/modules that we want to load.
+  apacheModules =
+    [ # HTTP authentication mechanisms: basic and digest.
+      "auth_basic" "auth_digest"
+
+      # Authentication: is the user who he claims to be?
+      "authn_file" "authn_dbm" "authn_anon"
+      (if version24 then "authn_core" else "authn_alias")
+
+      # Authorization: is the user allowed access?
+      "authz_user" "authz_groupfile" "authz_host"
+
+      # Other modules.
+      "ext_filter" "include" "log_config" "env" "mime_magic"
+      "cern_meta" "expires" "headers" "usertrack" /* "unique_id" */ "setenvif"
+      "mime" "dav" "status" "autoindex" "asis" "info" "dav_fs"
+      "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling"
+      "userdir" "alias" "rewrite" "proxy" "proxy_http"
+    ]
+    ++ optionals version24 [
+      "mpm_${mainCfg.multiProcessingModule}"
+      "authz_core"
+      "unixd"
+      "cache" "cache_disk"
+      "slotmem_shm"
+      "socache_shmcb"
+      # For compatibility with old configurations, the new module mod_access_compat is provided.
+      "access_compat"
+    ]
+    ++ (if mainCfg.multiProcessingModule == "prefork" then [ "cgi" ] else [ "cgid" ])
+    ++ optional enableSSL "ssl"
+    ++ extraApacheModules;
+
+
+  allDenied = if version24 then ''
+    Require all denied
+  '' else ''
+    Order deny,allow
+    Deny from all
+  '';
+
+  allGranted = if version24 then ''
+    Require all granted
+  '' else ''
+    Order allow,deny
+    Allow from all
+  '';
+
+
+  loggingConf = (if mainCfg.logFormat != "none" then ''
+    ErrorLog ${mainCfg.logDir}/error.log
+
+    LogLevel notice
+
+    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+    LogFormat "%h %l %u %t \"%r\" %>s %b" common
+    LogFormat "%{Referer}i -> %U" referer
+    LogFormat "%{User-agent}i" agent
+
+    CustomLog ${mainCfg.logDir}/access.log ${mainCfg.logFormat}
+  '' else ''
+    ErrorLog /dev/null
+  '');
+
+
+  browserHacks = ''
+    BrowserMatch "Mozilla/2" nokeepalive
+    BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+    BrowserMatch "RealPlayer 4\.0" force-response-1.0
+    BrowserMatch "Java/1\.0" force-response-1.0
+    BrowserMatch "JDK/1\.0" force-response-1.0
+    BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
+    BrowserMatch "^WebDrive" redirect-carefully
+    BrowserMatch "^WebDAVFS/1.[012]" redirect-carefully
+    BrowserMatch "^gnome-vfs" redirect-carefully
+  '';
+
+
+  sslConf = ''
+    SSLSessionCache ${if version24 then "shmcb" else "shm"}:${mainCfg.stateDir}/ssl_scache(512000)
+
+    ${if version24 then "Mutex" else "SSLMutex"} posixsem
+
+    SSLRandomSeed startup builtin
+    SSLRandomSeed connect builtin
+
+    SSLProtocol ${mainCfg.sslProtocols}
+    SSLCipherSuite ${mainCfg.sslCiphers}
+    SSLHonorCipherOrder on
+  '';
+
+
+  mimeConf = ''
+    TypesConfig ${httpd}/conf/mime.types
+
+    AddType application/x-x509-ca-cert .crt
+    AddType application/x-pkcs7-crl    .crl
+    AddType application/x-httpd-php    .php .phtml
+
+    <IfModule mod_mime_magic.c>
+        MIMEMagicFile ${httpd}/conf/magic
+    </IfModule>
+  '';
+
+
+  perServerConf = isMainServer: cfg: let
+
+    serverInfo = makeServerInfo cfg;
+
+    subservices = callSubservices serverInfo cfg.extraSubservices;
+
+    maybeDocumentRoot = fold (svc: acc:
+      if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
+    ) null ([ cfg ] ++ subservices);
+
+    documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
+      pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
+
+    documentRootConf = ''
+      DocumentRoot "${documentRoot}"
+
+      <Directory "${documentRoot}">
+          Options Indexes FollowSymLinks
+          AllowOverride None
+          ${allGranted}
+      </Directory>
+    '';
+
+    robotsTxt =
+      concatStringsSep "\n" (filter (x: x != "") (
+        # If this is a vhost, the include the entries for the main server as well.
+        (if isMainServer then [] else [mainCfg.robotsEntries] ++ map (svc: svc.robotsEntries) mainSubservices)
+        ++ [cfg.robotsEntries]
+        ++ (map (svc: svc.robotsEntries) subservices)));
+
+  in ''
+    ${concatStringsSep "\n" (map (n: "ServerName ${n}") serverInfo.canonicalNames)}
+
+    ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
+
+    ${if cfg.sslServerCert != null then ''
+      SSLCertificateFile ${cfg.sslServerCert}
+      SSLCertificateKeyFile ${cfg.sslServerKey}
+      ${if cfg.sslServerChain != null then ''
+        SSLCertificateChainFile ${cfg.sslServerChain}
+      '' else ""}
+    '' else ""}
+
+    ${if cfg.enableSSL then ''
+      SSLEngine on
+    '' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
+    ''
+      SSLEngine off
+    '' else ""}
+
+    ${if isMainServer || cfg.adminAddr != null then ''
+      ServerAdmin ${cfg.adminAddr}
+    '' else ""}
+
+    ${if !isMainServer && mainCfg.logPerVirtualHost then ''
+      ErrorLog ${mainCfg.logDir}/error-${cfg.hostName}.log
+      CustomLog ${mainCfg.logDir}/access-${cfg.hostName}.log ${cfg.logFormat}
+    '' else ""}
+
+    ${optionalString (robotsTxt != "") ''
+      Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt}
+    ''}
+
+    ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
+
+    ${if cfg.enableUserDir then ''
+
+      UserDir public_html
+      UserDir disabled root
+
+      <Directory "/home/*/public_html">
+          AllowOverride FileInfo AuthConfig Limit Indexes
+          Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+          <Limit GET POST OPTIONS>
+              ${allGranted}
+          </Limit>
+          <LimitExcept GET POST OPTIONS>
+              ${allDenied}
+          </LimitExcept>
+      </Directory>
+
+    '' else ""}
+
+    ${if cfg.globalRedirect != null && cfg.globalRedirect != "" then ''
+      RedirectPermanent / ${cfg.globalRedirect}
+    '' else ""}
+
+    ${
+      let makeFileConf = elem: ''
+            Alias ${elem.urlPath} ${elem.file}
+          '';
+      in concatMapStrings makeFileConf cfg.servedFiles
+    }
+
+    ${
+      let makeDirConf = elem: ''
+            Alias ${elem.urlPath} ${elem.dir}/
+            <Directory ${elem.dir}>
+                Options +Indexes
+                ${allGranted}
+                AllowOverride All
+            </Directory>
+          '';
+      in concatMapStrings makeDirConf cfg.servedDirs
+    }
+
+    ${concatMapStrings (svc: svc.extraConfig) subservices}
+
+    ${cfg.extraConfig}
+  '';
+
+
+  confFile = pkgs.writeText "httpd.conf" ''
+
+    ServerRoot ${httpd}
+
+    ${optionalString version24 ''
+      DefaultRuntimeDir ${mainCfg.stateDir}/runtime
+    ''}
+
+    PidFile ${mainCfg.stateDir}/httpd.pid
+
+    ${optionalString (mainCfg.multiProcessingModule != "prefork") ''
+      # mod_cgid requires this.
+      ScriptSock ${mainCfg.stateDir}/cgisock
+    ''}
+
+    <IfModule prefork.c>
+        MaxClients           ${toString mainCfg.maxClients}
+        MaxRequestsPerChild  ${toString mainCfg.maxRequestsPerChild}
+    </IfModule>
+
+    ${let
+        listen = concatMap getListen allHosts;
+        toStr = listen: "Listen ${listenToString listen}\n";
+        uniqueListen = uniqList {inputList = map toStr listen;};
+      in concatStrings uniqueListen
+    }
+
+    User ${mainCfg.user}
+    Group ${mainCfg.group}
+
+    ${let
+        load = {name, path}: "LoadModule ${name}_module ${path}\n";
+        allModules =
+          concatMap (svc: svc.extraModulesPre) allSubservices
+          ++ map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules
+          ++ optional mainCfg.enableMellon { name = "auth_mellon"; path = "${pkgs.apacheHttpdPackages.mod_auth_mellon}/modules/mod_auth_mellon.so"; }
+          ++ optional enablePHP { name = "php${phpMajorVersion}"; path = "${php}/modules/libphp${phpMajorVersion}.so"; }
+          ++ optional enablePerl { name = "perl"; path = "${mod_perl}/modules/mod_perl.so"; }
+          ++ concatMap (svc: svc.extraModules) allSubservices
+          ++ extraForeignModules;
+      in concatMapStrings load allModules
+    }
+
+    AddHandler type-map var
+
+    <Files ~ "^\.ht">
+        ${allDenied}
+    </Files>
+
+    ${mimeConf}
+    ${loggingConf}
+    ${browserHacks}
+
+    Include ${httpd}/conf/extra/httpd-default.conf
+    Include ${httpd}/conf/extra/httpd-autoindex.conf
+    Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf
+    Include ${httpd}/conf/extra/httpd-languages.conf
+
+    TraceEnable off
+
+    ${if enableSSL then sslConf else ""}
+
+    # Fascist default - deny access to everything.
+    <Directory />
+        Options FollowSymLinks
+        AllowOverride None
+        ${allDenied}
+    </Directory>
+
+    # But do allow access to files in the store so that we don't have
+    # to generate <Directory> clauses for every generated file that we
+    # want to serve.
+    <Directory /nix/store>
+        ${allGranted}
+    </Directory>
+
+    # Generate directives for the main server.
+    ${perServerConf true mainCfg}
+
+    # Always enable virtual hosts; it doesn't seem to hurt.
+    ${let
+        listen = concatMap getListen allHosts;
+        uniqueListen = uniqList {inputList = listen;};
+        directives = concatMapStrings (listen: "NameVirtualHost ${listenToString listen}\n") uniqueListen;
+      in optionalString (!version24) directives
+    }
+
+    ${let
+        makeVirtualHost = vhost: ''
+          <VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
+              ${perServerConf false vhost}
+          </VirtualHost>
+        '';
+      in concatMapStrings makeVirtualHost mainCfg.virtualHosts
+    }
+  '';
+
+
+  enablePHP = mainCfg.enablePHP || any (svc: svc.enablePHP) allSubservices;
+
+  enablePerl = mainCfg.enablePerl || any (svc: svc.enablePerl) allSubservices;
+
+
+  # Generate the PHP configuration file.  Should probably be factored
+  # out into a separate module.
+  phpIni = pkgs.runCommand "php.ini"
+    { options = concatStringsSep "\n"
+        ([ mainCfg.phpOptions ] ++ (map (svc: svc.phpOptions) allSubservices));
+      preferLocalBuild = true;
+    }
+    ''
+      cat ${php}/etc/php.ini > $out
+      echo "$options" >> $out
+    '';
+
+in
+
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.httpd = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Apache HTTP Server.";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.apacheHttpd;
+        defaultText = "pkgs.apacheHttpd";
+        description = ''
+          Overridable attribute of the Apache HTTP Server package to use.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        default = confFile;
+        defaultText = "confFile";
+        example = literalExample ''pkgs.writeText "httpd.conf" "# my custom config file ..."'';
+        description = ''
+          Override the configuration file used by Apache. By default,
+          NixOS generates one automatically.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Cnfiguration lines appended to the generated Apache
+          configuration file. Note that this mechanism may not work
+          when <option>configFile</option> is overridden.
+        '';
+      };
+
+      extraModules = mkOption {
+        type = types.listOf types.unspecified;
+        default = [];
+        example = literalExample ''[ "proxy_connect" { name = "php5"; path = "''${pkgs.php}/modules/libphp5.so"; } ]'';
+        description = ''
+          Additional Apache modules to be used.  These can be
+          specified as a string in the case of modules distributed
+          with Apache, or as an attribute set specifying the
+          <varname>name</varname> and <varname>path</varname> of the
+          module.
+        '';
+      };
+
+      logPerVirtualHost = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, each virtual host gets its own
+          <filename>access.log</filename> and
+          <filename>error.log</filename>, namely suffixed by the
+          <option>hostName</option> of the virtual host.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "wwwrun";
+        description = ''
+          User account under which httpd runs.  The account is created
+          automatically if it doesn't exist.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "wwwrun";
+        description = ''
+          Group under which httpd runs.  The account is created
+          automatically if it doesn't exist.
+        '';
+      };
+
+      logDir = mkOption {
+        type = types.path;
+        default = "/var/log/httpd";
+        description = ''
+          Directory for Apache's log files.  It is created automatically.
+        '';
+      };
+
+      stateDir = mkOption {
+        type = types.path;
+        default = "/run/httpd";
+        description = ''
+          Directory for Apache's transient runtime state (such as PID
+          files).  It is created automatically.  Note that the default,
+          <filename>/run/httpd</filename>, is deleted at boot time.
+        '';
+      };
+
+      virtualHosts = mkOption {
+        type = types.listOf (types.submodule (
+          { options = import ./per-server-options.nix {
+              inherit lib;
+              forMainServer = false;
+            };
+          }));
+        default = [];
+        example = [
+          { hostName = "foo";
+            documentRoot = "/data/webroot-foo";
+          }
+          { hostName = "bar";
+            documentRoot = "/data/webroot-bar";
+          }
+        ];
+        description = ''
+          Specification of the virtual hosts served by Apache.  Each
+          element should be an attribute set specifying the
+          configuration of the virtual host.  The available options
+          are the non-global options permissible for the main host.
+        '';
+      };
+
+      enableMellon = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the mod_auth_mellon module.";
+      };
+
+      enablePHP = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the PHP module.";
+      };
+
+      phpPackage = mkOption {
+        type = types.package;
+        default = pkgs.php;
+        defaultText = "pkgs.php";
+        description = ''
+          Overridable attribute of the PHP package to use.
+        '';
+      };
+
+      enablePerl = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether to enable the Perl module (mod_perl).";
+      };
+
+      phpOptions = mkOption {
+        type = types.lines;
+        default = "";
+        example =
+          ''
+            date.timezone = "CET"
+          '';
+        description =
+          "Options appended to the PHP configuration file <filename>php.ini</filename>.";
+      };
+
+      multiProcessingModule = mkOption {
+        type = types.str;
+        default = "prefork";
+        example = "worker";
+        description =
+          ''
+            Multi-processing module to be used by Apache.  Available
+            modules are <literal>prefork</literal> (the default;
+            handles each request in a separate child process),
+            <literal>worker</literal> (hybrid approach that starts a
+            number of child processes each running a number of
+            threads) and <literal>event</literal> (a recent variant of
+            <literal>worker</literal> that handles persistent
+            connections more efficiently).
+          '';
+      };
+
+      maxClients = mkOption {
+        type = types.int;
+        default = 150;
+        example = 8;
+        description = "Maximum number of httpd processes (prefork)";
+      };
+
+      maxRequestsPerChild = mkOption {
+        type = types.int;
+        default = 0;
+        example = 500;
+        description =
+          "Maximum number of httpd requests answered per httpd child (prefork), 0 means unlimited";
+      };
+
+      sslCiphers = mkOption {
+        type = types.str;
+        default = "HIGH:!aNULL:!MD5:!EXP";
+        description = "Cipher Suite available for negotiation in SSL proxy handshake.";
+      };
+
+      sslProtocols = mkOption {
+        type = types.str;
+        default = "All -SSLv2 -SSLv3 -TLSv1";
+        example = "All -SSLv2 -SSLv3";
+        description = "Allowed SSL/TLS protocol versions.";
+      };
+    }
+
+    # Include the options shared between the main server and virtual hosts.
+    // (import ./per-server-options.nix {
+      inherit lib;
+      forMainServer = true;
+    });
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.httpd.enable {
+
+    assertions = [ { assertion = mainCfg.enableSSL == true
+                               -> mainCfg.sslServerCert != null
+                                    && mainCfg.sslServerKey != null;
+                     message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
+                 ];
+
+    warnings = map (cfg: ''apache-httpd's port option is deprecated. Use listen = [{/*ip = "*"; */ port = ${toString cfg.port};}]; instead'' ) (lib.filter (cfg: cfg.port != 0) allHosts);
+
+    users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
+      { name = "wwwrun";
+        group = mainCfg.group;
+        description = "Apache httpd user";
+        uid = config.ids.uids.wwwrun;
+      });
+
+    users.groups = optionalAttrs (mainCfg.group == "wwwrun") (singleton
+      { name = "wwwrun";
+        gid = config.ids.gids.wwwrun;
+      });
+
+    environment.systemPackages = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices;
+
+    services.httpd.phpOptions =
+      ''
+        ; Needed for PHP's mail() function.
+        sendmail_path = sendmail -t -i
+
+        ; Don't advertise PHP
+        expose_php = off
+      '' + optionalString (!isNull config.time.timeZone) ''
+
+        ; Apparently PHP doesn't use $TZ.
+        date.timezone = "${config.time.timeZone}"
+      '';
+
+    systemd.services.httpd =
+      { description = "Apache HTTPD";
+
+        wantedBy = [ "multi-user.target" ];
+        wants = [ "keys.target" ];
+        after = [ "network.target" "fs.target" "postgresql.service" "keys.target" ];
+
+        path =
+          [ httpd pkgs.coreutils pkgs.gnugrep ]
+          ++ optional enablePHP pkgs.system-sendmail # Needed for PHP's mail() function.
+          ++ concatMap (svc: svc.extraServerPath) allSubservices;
+
+        environment =
+          optionalAttrs enablePHP { PHPRC = phpIni; }
+          // optionalAttrs mainCfg.enableMellon { LD_LIBRARY_PATH  = "${pkgs.xmlsec}/lib"; }
+          // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
+
+        preStart =
+          ''
+            mkdir -m 0750 -p ${mainCfg.stateDir}
+            [ $(id -u) != 0 ] || chown root.${mainCfg.group} ${mainCfg.stateDir}
+            ${optionalString version24 ''
+              mkdir -m 0750 -p "${mainCfg.stateDir}/runtime"
+              [ $(id -u) != 0 ] || chown root.${mainCfg.group} "${mainCfg.stateDir}/runtime"
+            ''}
+            mkdir -m 0700 -p ${mainCfg.logDir}
+
+            # Get rid of old semaphores.  These tend to accumulate across
+            # server restarts, eventually preventing it from restarting
+            # successfully.
+            for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do
+                ${pkgs.utillinux}/bin/ipcrm -s $i
+            done
+
+            # Run the startup hooks for the subservices.
+            for i in ${toString (map (svn: svn.startupScript) allSubservices)}; do
+                echo Running Apache startup hook $i...
+                $i
+            done
+          '';
+
+        serviceConfig.ExecStart = "@${httpd}/bin/httpd httpd -f ${httpdConf}";
+        serviceConfig.ExecStop = "${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop";
+        serviceConfig.ExecReload = "${httpd}/bin/httpd -f ${httpdConf} -k graceful";
+        serviceConfig.Type = "forking";
+        serviceConfig.PIDFile = "${mainCfg.stateDir}/httpd.pid";
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = "5s";
+      };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix
new file mode 100644
index 000000000000..8c1ac8935a47
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/foswiki.nix
@@ -0,0 +1,78 @@
+{ config, pkgs, lib, serverInfo, ... }:
+let
+  inherit (pkgs) foswiki;
+  inherit (serverInfo.serverConfig) user group;
+  inherit (config) vardir;
+in
+{
+  options.vardir = lib.mkOption {
+    type = lib.types.path;
+    default = "/var/www/foswiki";
+    description = "The directory where variable foswiki data will be stored and served from.";
+  };
+
+  # TODO: this will probably need to be better customizable
+  extraConfig =
+    let httpd-conf = pkgs.runCommand "foswiki-httpd.conf"
+      { preferLocalBuild = true; }
+      ''
+        substitute '${foswiki}/foswiki_httpd_conf.txt' "$out" \
+          --replace /var/www/foswiki/ "${vardir}/"
+      '';
+    in
+      ''
+        RewriteEngine on
+        RewriteRule /foswiki/(.*) ${vardir}/$1
+
+        <Directory "${vardir}">
+          Require all granted
+        </Directory>
+
+        Include ${httpd-conf}
+        <Directory "${vardir}/pub">
+          Options FollowSymlinks
+        </Directory>
+      '';
+
+  /** This handles initial setup and updates.
+      It will probably need some tweaking, maybe per-site.  */
+  startupScript = pkgs.writeScript "foswiki_startup.sh" (
+    let storeLink = "${vardir}/package"; in
+    ''
+      [ -e '${storeLink}' ] || needs_setup=1
+      mkdir -p '${vardir}'
+      cd '${vardir}'
+      ln -sf -T '${foswiki}' '${storeLink}'
+
+      if [ -n "$needs_setup" ]; then # do initial setup
+        mkdir -p bin lib
+        # setup most of data/ as copies only
+        cp -r '${foswiki}'/data '${vardir}/'
+        rm -r '${vardir}'/data/{System,mime.types}
+        ln -sr -t '${vardir}/data/' '${storeLink}'/data/{System,mime.types}
+
+        ln -sr '${storeLink}/locale' .
+
+        mkdir pub
+        ln -sr '${storeLink}/pub/System' pub/
+
+        mkdir templates
+        ln -sr '${storeLink}'/templates/* templates/
+
+        ln -sr '${storeLink}/tools' .
+
+        mkdir -p '${vardir}'/working/{logs,tmp}
+        ln -sr '${storeLink}/working/README' working/ # used to check dir validity
+
+        chown -R '${user}:${group}' .
+        chmod +w -R .
+      fi
+
+      # bin/* and lib/* shall always be overwritten, in case files are added
+      ln -srf '${storeLink}'/bin/* '${vardir}/bin/'
+      ln -srf '${storeLink}'/lib/* '${vardir}/lib/'
+    ''
+    /* Symlinking bin/ one-by-one ensures that ${vardir}/lib/LocalSite.cfg
+        is used instead of ${foswiki}/... */
+  );
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/limesurvey.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/limesurvey.nix
new file mode 100644
index 000000000000..5c387700a5d5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/limesurvey.nix
@@ -0,0 +1,196 @@
+{ config, lib, pkgs, serverInfo, ... }:
+
+with lib;
+
+let
+
+  httpd = serverInfo.serverConfig.package;
+
+  version24 = !versionOlder httpd.version "2.4";
+
+  allGranted = if version24 then ''
+    Require all granted
+  '' else ''
+    Order allow,deny
+    Allow from all
+  '';
+
+  limesurveyConfig = pkgs.writeText "config.php" ''
+    <?php
+    $config = array();
+    $config['name']  = "${config.siteName}";
+    $config['runtimePath'] = "${config.dataDir}/tmp/runtime";
+    $config['components'] = array();
+    $config['components']['db'] = array();
+    $config['components']['db']['connectionString'] = '${config.dbType}:host=${config.dbHost};port=${toString config.dbPort};user=${config.dbUser};password=${config.dbPassword};dbname=${config.dbName};';
+    $config['components']['db']['username'] = '${config.dbUser}';
+    $config['components']['db']['password'] = '${config.dbPassword}';
+    $config['components']['db']['charset'] = 'utf-8';
+    $config['components']['db']['tablePrefix'] = "prefix_";
+    $config['components']['assetManager'] = array();
+    $config['components']['assetManager']['basePath'] = '${config.dataDir}/tmp/assets';
+    $config['config'] = array();
+    $config['config']['debug'] = 1;
+    $config['config']['tempdir']  = "${config.dataDir}/tmp";
+    $config['config']['tempdir']  = "${config.dataDir}/tmp";
+    $config['config']['uploaddir']  = "${config.dataDir}/upload";
+    $config['config']['force_ssl'] = '${if config.forceSSL then "on" else ""}';
+    $config['config']['defaultlang'] = '${config.defaultLang}';
+    return $config;
+    ?>
+  '';
+
+  limesurveyRoot = "${pkgs.limesurvey}/share/limesurvey/";
+
+in rec {
+
+  extraConfig = ''
+    Alias ${config.urlPrefix}/tmp ${config.dataDir}/tmp
+
+    <Directory ${config.dataDir}/tmp>
+      ${allGranted}
+      Options -Indexes +FollowSymlinks
+    </Directory>
+
+    Alias ${config.urlPrefix}/upload ${config.dataDir}/upload
+
+    <Directory ${config.dataDir}/upload>
+      ${allGranted}
+      Options -Indexes
+    </Directory>
+
+    ${if config.urlPrefix != "" then ''
+      Alias ${config.urlPrefix} ${limesurveyRoot}
+    '' else ''
+      RewriteEngine On
+      RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
+      RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
+    ''}
+
+    <Directory ${limesurveyRoot}>
+      DirectoryIndex index.php
+    </Directory>
+  '';
+
+  globalEnvVars = [
+    { name = "LIMESURVEY_CONFIG"; value = limesurveyConfig; }
+  ];
+
+  documentRoot = if config.urlPrefix == "" then limesurveyRoot else null;
+
+  enablePHP = true;
+
+  options = {
+
+    id = mkOption {
+      default = "main";
+      description = ''
+        A unique identifier necessary to keep multiple Limesurvey server
+        instances on the same machine apart.  This is used to
+        disambiguate the administrative scripts, which get names like
+        mediawiki-$id-change-password.
+      '';
+    };
+
+    urlPrefix = mkOption {
+      default = "";
+      description = "Url prefix for site.";
+      type = types.str;
+    };
+
+    dbType = mkOption {
+      default = "pgsql";
+      description = "Type of database for limesurvey, for now, only pgsql.";
+      type = types.enum ["pgsql"];
+    };
+
+    dbName = mkOption {
+      default = "limesurvey";
+      description = "Name of the database that holds the limesurvey data.";
+      type = types.str;
+    };
+
+    dbHost = mkOption {
+      default = "localhost";
+      description = "Limesurvey database host.";
+      type = types.str;
+    };
+
+    dbPort = mkOption {
+      default = 5432;
+      description = "Limesurvey database port.";
+      type = types.int;
+    };
+
+    dbUser = mkOption {
+      default = "limesurvey";
+      description = "Limesurvey database user.";
+      type = types.str;
+    };
+
+    dbPassword = mkOption {
+      example = "foobar";
+      description = "Limesurvey database password.";
+      type = types.str;
+    };
+
+    adminUser = mkOption {
+      description = "Limesurvey admin username.";
+      default = "admin";
+      type = types.str;
+    };
+
+    adminPassword = mkOption {
+      description = "Default limesurvey admin password.";
+      default = "admin";
+      type = types.str;
+    };
+
+    adminEmail = mkOption {
+      description = "Limesurvey admin email.";
+      default = "admin@admin.com";
+      type = types.str;
+    };
+
+    forceSSL = mkOption {
+      default = false;
+      description = "Force use of HTTPS connection.";
+      type = types.bool;
+    };
+
+    siteName = mkOption {
+      default = "LimeSurvey";
+      description = "LimeSurvey name of the site.";
+      type = types.str;
+    };
+
+    defaultLang = mkOption {
+      default = "en";
+      description = "LimeSurvey default language.";
+      type = types.str;
+    };
+
+    dataDir = mkOption {
+      default = "/var/lib/limesurvey";
+      description = "LimeSurvey data directory.";
+      type = types.path;
+    };
+  };
+
+  startupScript = pkgs.writeScript "limesurvey_startup.sh" ''
+    if [ ! -f ${config.dataDir}/.created ]; then
+      mkdir -p ${config.dataDir}/{tmp/runtime,tmp/assets,tmp/upload,upload}
+      chmod -R ug+rw ${config.dataDir}
+      chmod -R o-rwx ${config.dataDir}
+      chown -R wwwrun:wwwrun ${config.dataDir}
+
+      ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
+      ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}" || true
+      ${pkgs.sudo}/bin/sudo -u postgres ${pkgs.postgresql}/bin/psql -U postgres -d postgres -c "alter user ${config.dbUser} with password '${config.dbPassword}';" || true
+
+      ${pkgs.limesurvey}/bin/limesurvey-console install '${config.adminUser}' '${config.adminPassword}' '${config.adminUser}' '${config.adminEmail}'
+
+      touch ${config.dataDir}/.created
+    fi
+  '';
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
new file mode 100644
index 000000000000..6234478014ce
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
@@ -0,0 +1,349 @@
+{ config, lib, pkgs, serverInfo, php, ... }:
+
+with lib;
+
+let
+
+  httpd = serverInfo.serverConfig.package;
+
+  version24 = !versionOlder httpd.version "2.4";
+
+  allGranted = if version24 then ''
+    Require all granted
+  '' else ''
+    Order allow,deny
+    Allow from all
+  '';
+
+  mediawikiConfig = pkgs.writeText "LocalSettings.php"
+    ''
+      <?php
+        # Copied verbatim from the default (generated) LocalSettings.php.
+        if( defined( 'MW_INSTALL_PATH' ) ) {
+                $IP = MW_INSTALL_PATH;
+        } else {
+                $IP = dirname( __FILE__ );
+        }
+
+        $path = array( $IP, "$IP/includes", "$IP/languages" );
+        set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() );
+
+        require_once( "$IP/includes/DefaultSettings.php" );
+
+        if ( $wgCommandLineMode ) {
+                if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
+                        die( "This script must be run from the command line\n" );
+                }
+        }
+
+        $wgScriptPath = "${config.urlPrefix}";
+
+        # We probably need to set $wgSecretKey and $wgCacheEpoch.
+
+        # Paths to external programs.
+        $wgDiff3 = "${pkgs.diffutils}/bin/diff3";
+        $wgDiff = "${pkgs.diffutils}/bin/diff";
+        $wgImageMagickConvertCommand = "${pkgs.imagemagick.out}/bin/convert";
+
+        #$wgDebugLogFile = "/tmp/mediawiki_debug_log.txt";
+
+        # Database configuration.
+        $wgDBtype = "${config.dbType}";
+        $wgDBserver = "${config.dbServer}";
+        $wgDBuser = "${config.dbUser}";
+        $wgDBpassword = "${config.dbPassword}";
+        $wgDBname = "${config.dbName}";
+
+        # E-mail.
+        $wgEmergencyContact = "${config.emergencyContact}";
+        $wgPasswordSender = "${config.passwordSender}";
+
+        $wgSitename = "${config.siteName}";
+
+        ${optionalString (config.logo != "") ''
+          $wgLogo = "${config.logo}";
+        ''}
+
+        ${optionalString (config.articleUrlPrefix != "") ''
+          $wgArticlePath = "${config.articleUrlPrefix}/$1";
+        ''}
+
+        ${optionalString config.enableUploads ''
+          $wgEnableUploads = true;
+          $wgUploadDirectory = "${config.uploadDir}";
+        ''}
+
+        ${optionalString (config.defaultSkin != "") ''
+          $wgDefaultSkin = "${config.defaultSkin}";
+        ''}
+
+        ${config.extraConfig}
+      ?>
+    '';
+
+  # Unpack Mediawiki and put the config file in its root directory.
+  mediawikiRoot = pkgs.stdenv.mkDerivation rec {
+    name= "mediawiki-1.31.1";
+
+    src = pkgs.fetchurl {
+      url = "https://releases.wikimedia.org/mediawiki/1.31/${name}.tar.gz";
+      sha256 = "13x48clij21cmysjkpnx68vggchrdasqp7b290j87xlfgjhdhnnf";
+    };
+
+    skins = config.skins;
+    extensions = config.extensions;
+
+    buildPhase =
+      ''
+        for skin in $skins; do
+          cp -prvd $skin/* skins/
+        done
+        for extension in $extensions; do
+          cp -prvd $extension/* extensions/
+        done
+      ''; # */
+
+    installPhase =
+      ''
+        mkdir -p $out
+        cp -r * $out
+        cp ${mediawikiConfig} $out/LocalSettings.php
+        sed -i \
+        -e 's|/bin/bash|${pkgs.bash}/bin/bash|g' \
+        -e 's|/usr/bin/timeout|${pkgs.coreutils}/bin/timeout|g' \
+          $out/includes/shell/limit.sh \
+          $out/includes/GlobalFunctions.php
+      '';
+  };
+
+  mediawikiScripts = pkgs.runCommand "mediawiki-${config.id}-scripts" {
+      buildInputs = [ pkgs.makeWrapper ];
+      preferLocalBuild = true;
+    } ''
+      mkdir -p $out/bin
+      for i in changePassword.php createAndPromote.php userOptions.php edit.php nukePage.php update.php; do
+        makeWrapper ${php}/bin/php $out/bin/mediawiki-${config.id}-$(basename $i .php) \
+          --add-flags ${mediawikiRoot}/maintenance/$i
+      done
+    '';
+
+in
+
+{
+
+  extraConfig =
+    ''
+      ${optionalString config.enableUploads ''
+        Alias ${config.urlPrefix}/images ${config.uploadDir}
+
+        <Directory ${config.uploadDir}>
+            ${allGranted}
+            Options -Indexes
+        </Directory>
+      ''}
+
+      ${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else ''
+        RewriteEngine On
+        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
+        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
+        ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedDirs}
+        ${concatMapStringsSep "\n" (u: "RewriteCond %{REQUEST_URI} !^${u.urlPath}") serverInfo.vhostConfig.servedFiles}
+        RewriteRule ${if config.enableUploads
+          then "!^/images"
+          else "^.*\$"
+        } %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == ""
+          then ""
+          else "${config.articleUrlPrefix}/"
+        }index.php [L]
+      ''}
+
+      <Directory ${mediawikiRoot}>
+          ${allGranted}
+          DirectoryIndex index.php
+      </Directory>
+
+      ${optionalString (config.articleUrlPrefix != "") ''
+        Alias ${config.articleUrlPrefix} ${mediawikiRoot}/index.php
+      ''}
+    '';
+
+  documentRoot = if config.urlPrefix == "" then mediawikiRoot else null;
+
+  enablePHP = true;
+
+  options = {
+
+    id = mkOption {
+      default = "main";
+      description = ''
+        A unique identifier necessary to keep multiple MediaWiki server
+        instances on the same machine apart.  This is used to
+        disambiguate the administrative scripts, which get names like
+        mediawiki-$id-change-password.
+      '';
+    };
+
+    dbType = mkOption {
+      default = "postgres";
+      example = "mysql";
+      description = "Database type.";
+    };
+
+    dbName = mkOption {
+      default = "mediawiki";
+      description = "Name of the database that holds the MediaWiki data.";
+    };
+
+    dbServer = mkOption {
+      default = ""; # use a Unix domain socket
+      example = "10.0.2.2";
+      description = ''
+        The location of the database server.  Leave empty to use a
+        database server running on the same machine through a Unix
+        domain socket.
+      '';
+    };
+
+    dbUser = mkOption {
+      default = "mediawiki";
+      description = "The user name for accessing the database.";
+    };
+
+    dbPassword = mkOption {
+      default = "";
+      example = "foobar";
+      description = ''
+        The password of the database user.  Warning: this is stored in
+        cleartext in the Nix store!
+      '';
+    };
+
+    emergencyContact = mkOption {
+      default = serverInfo.serverConfig.adminAddr;
+      example = "admin@example.com";
+      description = ''
+        Emergency contact e-mail address.  Defaults to the Apache
+        admin address.
+      '';
+    };
+
+    passwordSender = mkOption {
+      default = serverInfo.serverConfig.adminAddr;
+      example = "password@example.com";
+      description = ''
+        E-mail address from which password confirmations originate.
+        Defaults to the Apache admin address.
+      '';
+    };
+
+    siteName = mkOption {
+      default = "MediaWiki";
+      example = "Foobar Wiki";
+      description = "Name of the wiki";
+    };
+
+    logo = mkOption {
+      default = "";
+      example = "/images/logo.png";
+      description = "The URL of the site's logo (which should be a 135x135px image).";
+    };
+
+    urlPrefix = mkOption {
+      default = "/w";
+      description = ''
+        The URL prefix under which the Mediawiki service appears.
+      '';
+    };
+
+    articleUrlPrefix = mkOption {
+      default = "/wiki";
+      example = "";
+      description = ''
+        The URL prefix under which article pages appear,
+        e.g. http://server/wiki/Page.  Leave empty to use the main URL
+        prefix, e.g. http://server/w/index.php?title=Page.
+      '';
+    };
+
+    enableUploads = mkOption {
+      default = false;
+      description = "Whether to enable file uploads.";
+    };
+
+    uploadDir = mkOption {
+      default = throw "You must specify `uploadDir'.";
+      example = "/data/mediawiki-upload";
+      description = "The directory that stores uploaded files.";
+    };
+
+    defaultSkin = mkOption {
+      default = "";
+      example = "nostalgia";
+      description = "Set this value to change the default skin used by MediaWiki.";
+    };
+
+    skins = mkOption {
+      default = [];
+      type = types.listOf types.path;
+      description =
+        ''
+          List of paths whose content is copied to the ‘skins’
+          subdirectory of the MediaWiki installation.
+        '';
+    };
+
+    extensions = mkOption {
+      default = [];
+      type = types.listOf types.path;
+      description =
+        ''
+          List of paths whose content is copied to the 'extensions'
+          subdirectory of the MediaWiki installation.
+        '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example =
+        ''
+          $wgEnableEmail = false;
+        '';
+      description = ''
+        Any additional text to be appended to MediaWiki's
+        configuration file.  This is a PHP script.  For configuration
+        settings, see <link xlink:href='https://www.mediawiki.org/wiki/Manual:Configuration_settings'/>.
+      '';
+    };
+
+  };
+
+  extraPath = [ mediawikiScripts ];
+
+  # !!! Need to specify that Apache has a dependency on PostgreSQL!
+
+  startupScript = pkgs.writeScript "mediawiki_startup.sh"
+    # Initialise the database automagically if we're using a Postgres
+    # server on localhost.
+    (optionalString (config.dbType == "postgres" && config.dbServer == "") ''
+      if ! ${pkgs.postgresql}/bin/psql -l | grep -q ' ${config.dbName} ' ; then
+          ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole "${config.dbUser}" || true
+          ${pkgs.postgresql}/bin/createdb "${config.dbName}" -O "${config.dbUser}"
+          ( echo 'CREATE LANGUAGE plpgsql;'
+            cat ${mediawikiRoot}/maintenance/postgres/tables.sql
+            echo 'CREATE TEXT SEARCH CONFIGURATION public.default ( COPY = pg_catalog.english );'
+            echo COMMIT
+          ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
+      fi
+      ${php}/bin/php ${mediawikiRoot}/maintenance/update.php
+    '');
+
+  robotsEntries = optionalString (config.articleUrlPrefix != "")
+    ''
+      User-agent: *
+      Disallow: ${config.urlPrefix}/
+      Disallow: ${config.articleUrlPrefix}/Special:Search
+      Disallow: ${config.articleUrlPrefix}/Special:Random
+    '';
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mercurial.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mercurial.nix
new file mode 100644
index 000000000000..4b8ee2b17ea7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/mercurial.nix
@@ -0,0 +1,75 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (pkgs) mercurial;
+  inherit (lib) mkOption;
+
+  urlPrefix = config.urlPrefix;
+
+  cgi = pkgs.stdenv.mkDerivation {
+    name = "mercurial-cgi";
+    buildCommand = ''
+      mkdir -p $out
+      cp -v ${mercurial}/share/cgi-bin/hgweb.cgi $out
+      sed -i "s|/path/to/repo/or/config|$out/hgweb.config|" $out/hgweb.cgi
+      echo "
+      [collections]
+      ${config.dataDir} = ${config.dataDir}
+      [web]
+      style = gitweb
+      allow_push = *
+      " > $out/hgweb.config
+    '';
+  };
+
+in {
+
+  extraConfig = ''
+    RewriteEngine on
+    RewriteRule /(.*) ${cgi}/hgweb.cgi/$1
+
+    <Location "${urlPrefix}">
+        AuthType Basic
+        AuthName "Mercurial repositories"
+        AuthUserFile ${config.dataDir}/hgusers
+        <LimitExcept GET>
+            Require valid-user
+        </LimitExcept>
+    </Location>
+    <Directory "${cgi}">
+        Order allow,deny
+        Allow from all
+        AllowOverride All
+        Options ExecCGI
+        AddHandler cgi-script .cgi
+        PassEnv PYTHONPATH
+    </Directory>
+  '';
+
+  robotsEntries = ''
+    User-agent: *
+    Disallow: ${urlPrefix}
+  '';
+
+  extraServerPath = [ pkgs.python ];
+
+  globalEnvVars = [ { name = "PYTHONPATH"; value = "${mercurial}/lib/${pkgs.python.libPrefix}/site-packages"; } ];
+
+  options = {
+    urlPrefix = mkOption {
+      default = "/hg";
+      description = "
+        The URL prefix under which the Mercurial service appears.
+        Use the empty string to have it appear in the server root.
+      ";
+    };
+
+    dataDir = mkOption {
+      example = "/data/mercurial";
+      description = "
+        Path to the directory that holds the repositories.
+      ";
+    };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
new file mode 100644
index 000000000000..4bbd041b6e04
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/per-server-options.nix
@@ -0,0 +1,188 @@
+# This file defines the options that can be used both for the Apache
+# main server configuration, and for the virtual hosts.  (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{ forMainServer, lib }:
+
+with lib;
+
+{
+
+  hostName = mkOption {
+    type = types.str;
+    default = "localhost";
+    description = "Canonical hostname for the server.";
+  };
+
+  serverAliases = mkOption {
+    type = types.listOf types.str;
+    default = [];
+    example = ["www.example.org" "www.example.org:8080" "example.org"];
+    description = ''
+      Additional names of virtual hosts served by this virtual host configuration.
+    '';
+  };
+
+  port = mkOption {
+    type = types.int;
+    default = 0;
+    description = ''
+      Port for the server. Option will be removed, use <option>listen</option> instead.
+  '';
+  };
+
+  listen = mkOption {
+     type = types.listOf (types.submodule (
+          {
+            options = {
+              port = mkOption {
+                type = types.int;
+                description = "port to listen on";
+              };
+              ip = mkOption {
+                type = types.string;
+                default = "*";
+                description = "Ip to listen on. 0.0.0.0 for ipv4 only, * for all.";
+              };
+            };
+          } ));
+    description = ''
+      List of { /* ip: "*"; */ port = 80;} to listen on
+    '';
+
+    default = [];
+  };
+
+  enableSSL = mkOption {
+    type = types.bool;
+    default = false;
+    description = "Whether to enable SSL (https) support.";
+  };
+
+  # Note: sslServerCert and sslServerKey can be left empty, but this
+  # only makes sense for virtual hosts (they will inherit from the
+  # main server).
+
+  sslServerCert = mkOption {
+    type = types.nullOr types.path;
+    default = null;
+    example = "/var/host.cert";
+    description = "Path to server SSL certificate.";
+  };
+
+  sslServerKey = mkOption {
+    type = types.path;
+    example = "/var/host.key";
+    description = "Path to server SSL certificate key.";
+  };
+
+  sslServerChain = mkOption {
+    type = types.nullOr types.path;
+    default = null;
+    example = "/var/ca.pem";
+    description = "Path to server SSL chain file.";
+  };
+
+  adminAddr = mkOption ({
+    type = types.nullOr types.str;
+    example = "admin@example.org";
+    description = "E-mail address of the server administrator.";
+  } // (if forMainServer then {} else {default = null;}));
+
+  documentRoot = mkOption {
+    type = types.nullOr types.path;
+    default = null;
+    example = "/data/webserver/docs";
+    description = ''
+      The path of Apache's document root directory.  If left undefined,
+      an empty directory in the Nix store will be used as root.
+    '';
+  };
+
+  servedDirs = mkOption {
+    type = types.listOf types.attrs;
+    default = [];
+    example = [
+      { urlPath = "/nix";
+        dir = "/home/eelco/Dev/nix-homepage";
+      }
+    ];
+    description = ''
+      This option provides a simple way to serve static directories.
+    '';
+  };
+
+  servedFiles = mkOption {
+    type = types.listOf types.attrs;
+    default = [];
+    example = [
+      { urlPath = "/foo/bar.png";
+        file = "/home/eelco/some-file.png";
+      }
+    ];
+    description = ''
+      This option provides a simple way to serve individual, static files.
+    '';
+  };
+
+  extraConfig = mkOption {
+    type = types.lines;
+    default = "";
+    example = ''
+      <Directory /home>
+        Options FollowSymlinks
+        AllowOverride All
+      </Directory>
+    '';
+    description = ''
+      These lines go to httpd.conf verbatim. They will go after
+      directories and directory aliases defined by default.
+    '';
+  };
+
+  extraSubservices = mkOption {
+    type = types.listOf types.unspecified;
+    default = [];
+    description = "Extra subservices to enable in the webserver.";
+  };
+
+  enableUserDir = mkOption {
+    type = types.bool;
+    default = false;
+    description = ''
+      Whether to enable serving <filename>~/public_html</filename> as
+      <literal>/~<replaceable>username</replaceable></literal>.
+    '';
+  };
+
+  globalRedirect = mkOption {
+    type = types.nullOr types.str;
+    default = null;
+    example = http://newserver.example.org/;
+    description = ''
+      If set, all requests for this host are redirected permanently to
+      the given URL.
+    '';
+  };
+
+  logFormat = mkOption {
+    type = types.str;
+    default = "common";
+    example = "combined";
+    description = ''
+      Log format for Apache's log files. Possible values are: combined, common, referer, agent.
+    '';
+  };
+
+  robotsEntries = mkOption {
+    type = types.lines;
+    default = "";
+    example = "Disallow: /foo/";
+    description = ''
+      Specification of pages to be ignored by web crawlers. See <link
+      xlink:href='http://www.robotstxt.org/'/> for details.
+    '';
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/phabricator.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/phabricator.nix
new file mode 100644
index 000000000000..efd4a7b5f0fb
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/phabricator.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  phabricatorRoot = pkgs.phabricator;
+in {
+
+  enablePHP = true;
+  extraApacheModules = [ "mod_rewrite" ];
+  DocumentRoot = "${phabricatorRoot}/phabricator/webroot";
+
+  options = {
+      git = mkOption {
+          default = true;
+          description = "Enable git repositories.";
+      };
+      mercurial = mkOption {
+          default = true;
+          description = "Enable mercurial repositories.";
+      };
+      subversion = mkOption {
+          default = true;
+          description = "Enable subversion repositories.";
+      };
+  };
+
+  extraConfig = ''
+      DocumentRoot ${phabricatorRoot}/phabricator/webroot
+
+      RewriteEngine on
+      RewriteRule ^/rsrc/(.*) - [L,QSA]
+      RewriteRule ^/favicon.ico - [L,QSA]
+      RewriteRule ^(.*)$ /index.php?__path__=$1 [B,L,QSA]
+  '';
+
+  extraServerPath = [
+      "${pkgs.which}"
+      "${pkgs.diffutils}"
+      ] ++
+      (if config.mercurial then ["${pkgs.mercurial}"] else []) ++
+      (if config.subversion then ["${pkgs.subversion.out}"] else []) ++
+      (if config.git then ["${pkgs.git}"] else []);
+
+  startupScript = pkgs.writeScript "activatePhabricator" ''
+      mkdir -p /var/repo
+      chown wwwrun /var/repo
+  '';
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix
new file mode 100644
index 000000000000..a883bb2b3433
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/tomcat-connector.nix
@@ -0,0 +1,103 @@
+{ config, pkgs, serverInfo, lib, ... }:
+
+let
+  extraWorkersProperties = lib.optionalString (config ? extraWorkersProperties) config.extraWorkersProperties;
+  
+  workersProperties = pkgs.writeText "workers.properties" ''
+# Define list of workers that will be used
+# for mapping requests
+# The configuration directives are valid
+# for the mod_jk version 1.2.18 and later
+#
+worker.list=loadbalancer,status
+
+# Define Node1
+# modify the host as your host IP or DNS name.
+worker.node1.port=8009
+worker.node1.host=localhost
+worker.node1.type=ajp13
+worker.node1.lbfactor=1
+
+# Load-balancing behaviour
+worker.loadbalancer.type=lb
+worker.loadbalancer.balance_workers=node1
+
+# Status worker for managing load balancer
+worker.status.type=status
+
+${extraWorkersProperties}
+  '';
+in
+{
+
+  options = {
+    extraWorkersProperties = lib.mkOption {
+      default = "";
+      description = "Additional configuration for the workers.properties file.";
+    };
+  };
+
+  extraModules = [
+    { name = "jk"; path = "${pkgs.tomcat_connectors}/modules/mod_jk.so"; }
+  ];
+
+  extraConfig = ''
+# Where to find workers.properties
+JkWorkersFile ${workersProperties}
+
+# Where to put jk logs
+JkLogFile ${serverInfo.serverConfig.logDir}/mod_jk.log
+
+# Set the jk log level [debug/error/info]
+JkLogLevel info
+
+# Select the log format
+JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
+
+# JkOptions indicates to send SSK KEY SIZE
+# Note: Changed from +ForwardURICompat.
+# See http://tomcat.apache.org/security-jk.html
+JkOptions +ForwardKeySize +ForwardURICompatUnparsed -ForwardDirectories
+
+# JkRequestLogFormat
+JkRequestLogFormat "%w %V %T"
+
+# Mount your applications
+JkMount /__application__/* loadbalancer
+
+# You can use external file for mount points.
+# It will be checked for updates each 60 seconds.
+# The format of the file is: /url=worker
+# /examples/*=loadbalancer
+#JkMountFile uriworkermap.properties
+
+# Add shared memory.
+# This directive is present with 1.2.10 and
+# later versions of mod_jk, and is needed for
+# for load balancing to work properly
+# Note: Replaced JkShmFile logs/jk.shm due to SELinux issues. Refer to
+# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=225452
+JkShmFile ${serverInfo.serverConfig.stateDir}/jk.shm
+
+# Static files in all Tomcat webapp context directories are served by apache
+JkAutoAlias /var/tomcat/webapps
+
+# All requests go to worker by default
+JkMount /* loadbalancer
+# Serve some static files using httpd
+#JkUnMount /*.html loadbalancer
+#JkUnMount /*.jpg  loadbalancer
+#JkUnMount /*.gif  loadbalancer
+#JkUnMount /*.css  loadbalancer
+#JkUnMount /*.png  loadbalancer
+#JkUnMount /*.js  loadbalancer
+
+# Add jkstatus for managing runtime data
+<Location /jkstatus/>
+JkMount status
+Order deny,allow
+Deny from all
+Allow from 127.0.0.1
+</Location>
+  '';
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix
new file mode 100644
index 000000000000..28b411a64b6f
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/trac.nix
@@ -0,0 +1,121 @@
+{ config, lib, pkgs, serverInfo, ... }:
+
+with lib;
+
+let
+
+  # Build a Subversion instance with Apache modules and Swig/Python bindings.
+  subversion = pkgs.subversion.override {
+    bdbSupport = true;
+    httpServer = true;
+    pythonBindings = true;
+    apacheHttpd = httpd;
+  };
+
+  httpd = serverInfo.serverConfig.package;
+
+  versionPre24 = versionOlder httpd.version "2.4";
+
+in
+
+{
+
+  options = {
+
+    projectsLocation = mkOption {
+      description = "URL path in which Trac projects can be accessed";
+      default = "/projects";
+    };
+
+    projects = mkOption {
+      description = "List of projects that should be provided by Trac. If they are not defined yet empty projects are created.";
+      default = [];
+      example =
+        [ { identifier = "myproject";
+            name = "My Project";
+            databaseURL="postgres://root:password@/tracdb";
+            subversionRepository="/data/subversion/myproject";
+          }
+        ];
+    };
+
+    user = mkOption {
+      default = "wwwrun";
+      description = "User account under which Trac runs.";
+    };
+
+    group = mkOption {
+      default = "wwwrun";
+      description = "Group under which Trac runs.";
+    };
+
+    ldapAuthentication = {
+      enable = mkOption {
+        default = false;
+        description = "Enable the ldap authentication in trac";
+      };
+
+      url = mkOption {
+        default = "ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)";
+        description = "URL of the LDAP authentication";
+      };
+
+      name = mkOption {
+        default = "Trac server";
+        description = "AuthName";
+      };
+    };
+
+  };
+
+  extraModules = singleton
+    { name = "python"; path = "${pkgs.mod_python}/modules/mod_python.so"; };
+
+  extraConfig = ''
+    <Location ${config.projectsLocation}>
+      SetHandler mod_python
+      PythonHandler trac.web.modpython_frontend
+      PythonOption TracEnvParentDir /var/trac/projects
+      PythonOption TracUriRoot ${config.projectsLocation}
+      PythonOption PYTHON_EGG_CACHE /var/trac/egg-cache
+    </Location>
+    ${if config.ldapAuthentication.enable then ''
+      <LocationMatch "^${config.projectsLocation}[^/]+/login$">
+        AuthType Basic
+        AuthName "${config.ldapAuthentication.name}"
+        AuthBasicProvider "ldap"
+        AuthLDAPURL "${config.ldapAuthentication.url}"
+        ${if versionPre24 then "authzldapauthoritative Off" else ""}
+        require valid-user
+      </LocationMatch>
+    '' else ""}
+  '';
+
+  globalEnvVars = singleton
+    { name = "PYTHONPATH";
+      value =
+        makeSearchPathOutput "lib" "lib/${pkgs.python.libPrefix}/site-packages"
+          [ pkgs.mod_python
+            pkgs.pythonPackages.trac
+            pkgs.pythonPackages.setuptools
+            pkgs.pythonPackages.genshi
+            pkgs.pythonPackages.psycopg2
+            subversion
+          ];
+    };
+
+  startupScript = pkgs.writeScript "activateTrac" ''
+    mkdir -p /var/trac
+    chown ${config.user}:${config.group} /var/trac
+
+    ${concatMapStrings (project:
+      ''
+        if [ ! -d /var/trac/${project.identifier} ]
+        then
+            export PYTHONPATH=${pkgs.pythonPackages.psycopg2}/lib/${pkgs.python.libPrefix}/site-packages
+            ${pkgs.pythonPackages.trac}/bin/trac-admin /var/trac/${project.identifier} initenv "${project.name}" "${project.databaseURL}" svn "${project.subversionRepository}"
+        fi
+      '' ) (config.projects)}
+  '';
+
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/wordpress.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
new file mode 100644
index 000000000000..c68bfd25f6a8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/wordpress.nix
@@ -0,0 +1,285 @@
+{ config, lib, pkgs, serverInfo, ... }:
+# http://codex.wordpress.org/Hardening_WordPress
+
+with lib;
+
+let
+  # Our bare-bones wp-config.php file using the above settings
+  wordpressConfig = pkgs.writeText "wp-config.php" ''
+    <?php
+    define('DB_NAME',     '${config.dbName}');
+    define('DB_USER',     '${config.dbUser}');
+    define('DB_PASSWORD', file_get_contents('${config.dbPasswordFile}'));
+    define('DB_HOST',     '${config.dbHost}');
+    define('DB_CHARSET',  'utf8');
+    $table_prefix  = '${config.tablePrefix}';
+    define('AUTOMATIC_UPDATER_DISABLED', true);
+    ${config.extraConfig}
+    if ( !defined('ABSPATH') )
+    	define('ABSPATH', dirname(__FILE__) . '/');
+    require_once(ABSPATH . 'wp-settings.php');
+  '';
+
+  # .htaccess to support pretty URLs
+  htaccess = pkgs.writeText "htaccess" ''
+    <IfModule mod_rewrite.c>
+    RewriteEngine On
+    RewriteBase /
+    RewriteRule ^index\.php$ - [L]
+
+    # add a trailing slash to /wp-admin
+    RewriteRule ^wp-admin$ wp-admin/ [R=301,L]
+
+    RewriteCond %{REQUEST_FILENAME} -f [OR]
+    RewriteCond %{REQUEST_FILENAME} -d
+    RewriteRule ^ - [L]
+    RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
+    RewriteRule ^(.*\.php)$ $1 [L]
+    RewriteRule . index.php [L]
+    </IfModule>
+
+    ${config.extraHtaccess}
+  '';
+
+  # WP translation can be found here:
+  #   https://github.com/nixcloud/wordpress-translations
+  supportedLanguages = {
+    en_GB = { revision="d6c005372a5318fd758b710b77a800c86518be13"; sha256="0qbbsi87k47q4rgczxx541xz4z4f4fr49hw4lnaxkdsf5maz8p9p"; };
+    de_DE = { revision="3c62955c27baaae98fd99feb35593d46562f4736"; sha256="1shndgd11dk836dakrjlg2arwv08vqx6j4xjh4jshvwmjab6ng6p"; };
+    zh_ZN = { revision="12b9f811e8cae4b6ee41de343d35deb0a8fdda6d"; sha256="1339ggsxh0g6lab37jmfxicsax4h702rc3fsvv5azs7mcznvwh47"; };
+    fr_FR = { revision="688c8b1543e3d38d9e8f57e0a6f2a2c3c8b588bd"; sha256="1j41iak0i6k7a4wzyav0yrllkdjjskvs45w53db8vfm8phq1n014"; };
+  };
+
+  downloadLanguagePack = language: revision: sha256s:
+    pkgs.stdenv.mkDerivation rec {
+      name = "wp_${language}";
+      src = pkgs.fetchFromGitHub {
+        owner = "nixcloud";
+        repo = "wordpress-translations";
+        rev = revision;
+        sha256 = sha256s;
+      };
+      installPhase = "mkdir -p $out; cp -R * $out/";
+    };
+
+  selectedLanguages = map (lang: downloadLanguagePack lang supportedLanguages.${lang}.revision supportedLanguages.${lang}.sha256) (config.languages);
+
+  # The wordpress package itself
+  wordpressRoot = pkgs.stdenv.mkDerivation rec {
+    name = "wordpress";
+    src = config.package;
+    installPhase = ''
+      mkdir -p $out
+      # copy all the wordpress files we downloaded
+      cp -R * $out/
+
+      # symlink the wordpress config
+      ln -s ${wordpressConfig} $out/wp-config.php
+      # symlink custom .htaccess
+      ln -s ${htaccess} $out/.htaccess
+      # symlink uploads directory
+      ln -s ${config.wordpressUploads} $out/wp-content/uploads
+
+      # remove bundled plugins(s) coming with wordpress
+      rm -Rf $out/wp-content/plugins/*
+      # remove bundled themes(s) coming with wordpress
+      rm -Rf $out/wp-content/themes/*
+
+      # copy additional theme(s)
+      ${concatMapStrings (theme: "cp -r ${theme} $out/wp-content/themes/${theme.name}\n") config.themes}
+      # copy additional plugin(s)
+      ${concatMapStrings (plugin: "cp -r ${plugin} $out/wp-content/plugins/${plugin.name}\n") (config.plugins) }
+
+      # symlink additional translation(s)
+      mkdir -p $out/wp-content/languages
+      ${concatMapStrings (language: "ln -s ${language}/*.mo ${language}/*.po $out/wp-content/languages/\n") (selectedLanguages) }
+    '';
+  };
+
+in
+
+{
+
+  # And some httpd extraConfig to make things work nicely
+  extraConfig = ''
+    <Directory ${wordpressRoot}>
+      DirectoryIndex index.php
+      Allow from *
+      Options FollowSymLinks
+      AllowOverride All
+    </Directory>
+  '';
+
+  enablePHP = true;
+
+  options = {
+    package = mkOption {
+      type = types.path;
+      default = pkgs.wordpress;
+      description = ''
+        Path to the wordpress sources.
+        Upgrading? We have a test! nix-build ./nixos/tests/wordpress.nix
+      '';
+    };
+    dbHost = mkOption {
+      default = "localhost";
+      description = "The location of the database server.";
+      example = "localhost";
+    };
+    dbName = mkOption {
+      default = "wordpress";
+      description = "Name of the database that holds the Wordpress data.";
+      example = "localhost";
+    };
+    dbUser = mkOption {
+      default = "wordpress";
+      description = "The dbUser, read: the username, for the database.";
+      example = "wordpress";
+    };
+    dbPassword = mkOption {
+      default = "wordpress";
+      description = ''
+        The mysql password to the respective dbUser.
+
+        Warning: this password is stored in the world-readable Nix store. It's
+        recommended to use the $dbPasswordFile option since that gives you control over
+        the security of the password. $dbPasswordFile also takes precedence over $dbPassword.
+      '';
+      example = "wordpress";
+    };
+    dbPasswordFile = mkOption {
+      type = types.str;
+      default = toString (pkgs.writeTextFile {
+        name = "wordpress-dbpassword";
+        text = config.dbPassword;
+      });
+      example = "/run/keys/wordpress-dbpassword";
+      description = ''
+        Path to a file that contains the mysql password to the respective dbUser.
+        The file should be readable by the user: config.services.httpd.user.
+
+        $dbPasswordFile takes precedence over the $dbPassword option.
+
+        This defaults to a file in the world-readable Nix store that contains the value
+        of the $dbPassword option. It's recommended to override this with a path not in
+        the Nix store. Tip: use nixops key management:
+        <link xlink:href='https://nixos.org/nixops/manual/#idm140737318306400'/>
+      '';
+    };
+    tablePrefix = mkOption {
+      default = "wp_";
+      description = ''
+        The $table_prefix is the value placed in the front of your database tables. Change the value if you want to use something other than wp_ for your database prefix. Typically this is changed if you are installing multiple WordPress blogs in the same database. See <link xlink:href='http://codex.wordpress.org/Editing_wp-config.php#table_prefix'/>.
+      '';
+    };
+    wordpressUploads = mkOption {
+    default = "/data/uploads";
+      description = ''
+        This directory is used for uploads of pictures and must be accessible (read: owned) by the httpd running user. The directory passed here is automatically created and permissions are given to the httpd running user.
+      '';
+    };
+    plugins = mkOption {
+      default = [];
+      type = types.listOf types.path;
+      description =
+        ''
+          List of path(s) to respective plugin(s) which are symlinked from the 'plugins' directory. Note: These plugins need to be packaged before use, see example.
+        '';
+      example = ''
+        # Wordpress plugin 'akismet' installation example
+        akismetPlugin = pkgs.stdenv.mkDerivation {
+          name = "akismet-plugin";
+          # Download the theme from the wordpress site
+          src = pkgs.fetchurl {
+            url = https://downloads.wordpress.org/plugin/akismet.3.1.zip;
+            sha256 = "1i4k7qyzna08822ncaz5l00wwxkwcdg4j9h3z2g0ay23q640pclg";
+          };
+          # We need unzip to build this package
+          buildInputs = [ pkgs.unzip ];
+          # Installing simply means copying all files to the output directory
+          installPhase = "mkdir -p $out; cp -R * $out/";
+        };
+
+        And then pass this theme to the themes list like this:
+          plugins = [ akismetPlugin ];
+      '';
+    };
+    themes = mkOption {
+      default = [];
+      type = types.listOf types.path;
+      description =
+        ''
+          List of path(s) to respective theme(s) which are symlinked from the 'theme' directory. Note: These themes need to be packaged before use, see example.
+        '';
+      example = ''
+        # For shits and giggles, let's package the responsive theme
+        responsiveTheme = pkgs.stdenv.mkDerivation {
+          name = "responsive-theme";
+          # Download the theme from the wordpress site
+          src = pkgs.fetchurl {
+            url = http://wordpress.org/themes/download/responsive.1.9.7.6.zip;
+            sha256 = "06i26xlc5kdnx903b1gfvnysx49fb4kh4pixn89qii3a30fgd8r8";
+          };
+          # We need unzip to build this package
+          buildInputs = [ pkgs.unzip ];
+          # Installing simply means copying all files to the output directory
+          installPhase = "mkdir -p $out; cp -R * $out/";
+        };
+
+        And then pass this theme to the themes list like this:
+          themes = [ responsiveTheme ];
+      '';
+    };
+    languages = mkOption {
+          default = [];
+          description = "Installs wordpress language packs based on the list, see wordpress.nix for possible translations.";
+          example = "[ \"en_GB\" \"de_DE\" ];";
+    };
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      example =
+        ''
+          define( 'AUTOSAVE_INTERVAL', 60 ); // Seconds
+        '';
+      description = ''
+        Any additional text to be appended to Wordpress's wp-config.php
+        configuration file.  This is a PHP script.  For configuration
+        settings, see <link xlink:href='http://codex.wordpress.org/Editing_wp-config.php'/>.
+      '';
+    };
+    extraHtaccess = mkOption {
+      default = "";
+      example =
+        ''
+          php_value upload_max_filesize 20M
+          php_value post_max_size 20M
+        '';
+      description = ''
+        Any additional text to be appended to Wordpress's .htaccess file.
+      '';
+    };
+  };
+
+  documentRoot = wordpressRoot;
+
+  # FIXME adding the user has to be done manually for the time being
+  startupScript = pkgs.writeScript "init-wordpress.sh" ''
+    #!/bin/sh
+    mkdir -p ${config.wordpressUploads}
+    chown ${serverInfo.serverConfig.user} ${config.wordpressUploads}
+
+    # we should use systemd dependencies here
+    if [ ! -d ${serverInfo.fullConfig.services.mysql.dataDir}/${config.dbName} ]; then
+      echo "Need to create the database '${config.dbName}' and grant permissions to user named '${config.dbUser}'."
+      # Wait until MySQL is up
+      while [ ! -e ${serverInfo.fullConfig.services.mysql.pidDir}/mysqld.pid ]; do
+        sleep 1
+      done
+      ${pkgs.mysql}/bin/mysql -e 'CREATE DATABASE ${config.dbName};'
+      ${pkgs.mysql}/bin/mysql -e "GRANT ALL ON ${config.dbName}.* TO ${config.dbUser}@localhost IDENTIFIED BY \"$(cat ${config.dbPasswordFile})\";"
+    else
+      echo "Good, no need to do anything database related."
+    fi
+  '';
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
new file mode 100644
index 000000000000..cab16593bcbc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/zabbix.nix
@@ -0,0 +1,84 @@
+{ config, lib, pkgs, serverInfo, ... }:
+
+with lib;
+
+let
+
+  # The Zabbix PHP frontend needs to be able to write its
+  # configuration settings (the connection info to the database) to
+  # the "conf" subdirectory.  So symlink $out/conf to some directory
+  # outside of the Nix store where we want to keep this stateful info.
+  # Note that different instances of the frontend will therefore end
+  # up with their own copies of the PHP sources.  !!! Alternatively,
+  # we could generate zabbix.conf.php declaratively.
+  zabbixPHP = pkgs.runCommand "${pkgs.zabbix.server.name}-php" {}
+    ''
+      cp -rs ${pkgs.zabbix.server}/share/zabbix/php "$out"
+      chmod -R u+w $out
+      ln -s "${if config.configFile == null
+               then "${config.stateDir}/zabbix.conf.php"
+               else config.configFile}" "$out/conf/zabbix.conf.php"
+    '';
+
+in
+
+{
+
+  enablePHP = true;
+
+  phpOptions =
+    ''
+      post_max_size = 32M
+      max_execution_time = 300
+      max_input_time = 300
+    '';
+
+  extraConfig = ''
+    Alias ${config.urlPrefix}/ ${zabbixPHP}/
+
+    <Directory ${zabbixPHP}>
+      DirectoryIndex index.php
+      Order deny,allow
+      Allow from *
+    </Directory>
+  '';
+
+  startupScript = pkgs.writeScript "zabbix-startup-hook" ''
+    mkdir -p ${config.stateDir}
+    chown -R ${serverInfo.serverConfig.user} ${config.stateDir}
+  '';
+
+  # The frontend needs "ps" to find out whether zabbix_server is running.
+  extraServerPath = [ pkgs.procps ];
+
+  options = {
+
+    urlPrefix = mkOption {
+      default = "/zabbix";
+      description = "
+        The URL prefix under which the Zabbix service appears.
+        Use the empty string to have it appear in the server root.
+      ";
+    };
+
+    configFile = mkOption {
+      default = null;
+      type = types.nullOr types.path;
+      description = ''
+        The configuration file (zabbix.conf.php) which contains the database
+        connection settings. If not set, the configuration settings will created
+        by the web installer.
+      '';
+    };
+
+    stateDir = mkOption {
+      default = "/var/lib/zabbix/frontend";
+      description = "
+        Directory where the dynamically generated configuration data
+        of the PHP frontend will be stored.
+      ";
+    };
+
+  };
+
+}