summary refs log tree commit diff
path: root/nixos/modules/services/databases
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services/databases')
-rw-r--r--nixos/modules/services/databases/4store-endpoint.nix72
-rw-r--r--nixos/modules/services/databases/4store.nix71
-rw-r--r--nixos/modules/services/databases/firebird.nix149
-rw-r--r--nixos/modules/services/databases/memcached.nix97
-rw-r--r--nixos/modules/services/databases/mongodb.nix130
-rw-r--r--nixos/modules/services/databases/mysql.nix245
-rw-r--r--nixos/modules/services/databases/mysql55.nix248
-rw-r--r--nixos/modules/services/databases/openldap.nix58
-rw-r--r--nixos/modules/services/databases/postgresql.nix232
-rw-r--r--nixos/modules/services/databases/redis.nix216
-rw-r--r--nixos/modules/services/databases/virtuoso.nix98
11 files changed, 1616 insertions, 0 deletions
diff --git a/nixos/modules/services/databases/4store-endpoint.nix b/nixos/modules/services/databases/4store-endpoint.nix
new file mode 100644
index 00000000000..7b03b4d8f1d
--- /dev/null
+++ b/nixos/modules/services/databases/4store-endpoint.nix
@@ -0,0 +1,72 @@
+{ config, pkgs, ... }:
+let
+  cfg = config.services.fourStoreEndpoint;
+  endpointUser = "fourstorehttp";
+  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${endpointUser} -c";
+in
+with pkgs.lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.fourStoreEndpoint = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable 4Store SPARQL endpoint.";
+      };
+
+      database = mkOption {
+        default = config.services.fourStore.database;
+        description = "RDF database name to expose via the endpoint. Defaults to local 4Store database name.";
+      };
+
+      listenAddress = mkOption {
+	default = null;
+        description = "IP address to listen on.";
+      };
+
+      port = mkOption {
+	default = 8080;
+        description = "port to listen on.";
+      };
+
+      options = mkOption {
+        default = "";
+        description = "Extra CLI options to pass to 4Store's 4s-httpd process.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (
+    mkAssert (cfg.enable -> cfg.database != "")
+      "Must specify database name" {
+
+    users.extraUsers = singleton
+      { name = endpointUser;
+        uid = config.ids.uids.fourStoreEndpoint;
+        description = "4Store SPARQL endpoint user";
+#        home = stateDir;
+      };
+
+    services.avahi.enable = true;
+
+    jobs.fourStoreEndpoint = {
+      name = "4store-endpoint";
+      startOn = "filesystem";
+
+      exec = ''
+	${run} '${pkgs.rdf4store}/bin/4s-httpd -D ${cfg.options} ${if cfg.listenAddress!=null then "-H ${cfg.listenAddress}" else "" } -p ${toString cfg.port} ${cfg.database}'
+      '';
+    };
+
+  });
+
+}
diff --git a/nixos/modules/services/databases/4store.nix b/nixos/modules/services/databases/4store.nix
new file mode 100644
index 00000000000..14990e92ea3
--- /dev/null
+++ b/nixos/modules/services/databases/4store.nix
@@ -0,0 +1,71 @@
+{ config, pkgs, ... }:
+let
+  cfg = config.services.fourStore;
+  stateDir = "/var/lib/4store";
+  fourStoreUser = "fourstore";
+  run = "${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${fourStoreUser}";
+in
+with pkgs.lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.fourStore = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable 4Store RDF database server.";
+      };
+
+      database = mkOption {
+        default = "";
+        description = "RDF database name. If it doesn't exist, it will be created. Databases are stored in ${stateDir}.";
+      };
+
+      options = mkOption {
+        default = "";
+        description = "Extra CLI options to pass to 4Store.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable (
+    mkAssert (cfg.enable -> cfg.database != "")
+      "Must specify database name" {
+
+    users.extraUsers = singleton
+      { name = fourStoreUser;
+        uid = config.ids.uids.fourStore;
+        description = "4Store database user";
+        home = stateDir;
+      };
+
+    services.avahi.enable = true;
+
+    jobs.fourStore = {
+      name = "4store";
+      startOn = "filesystem";
+
+      preStart = ''
+        mkdir -p ${stateDir}/
+        chown ${fourStoreUser} ${stateDir}
+	if ! test -e "${stateDir}/${cfg.database}"; then
+	  ${run} -c '${pkgs.rdf4store}/bin/4s-backend-setup ${cfg.database}'
+        fi
+      '';
+
+      exec = ''
+	${run} -c '${pkgs.rdf4store}/bin/4s-backend -D ${cfg.options} ${cfg.database}'
+      '';
+    };
+
+  });
+
+}
diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix
new file mode 100644
index 00000000000..aca0d58900b
--- /dev/null
+++ b/nixos/modules/services/databases/firebird.nix
@@ -0,0 +1,149 @@
+{ config, pkgs, ... }:
+
+# TODO: this file needs some additional work - at least you can connect to
+# firebird ..
+# Example how to connect:
+# isql /var/db/firebird/data/your-db.fdb -u sysdba -p <default password>
+
+# There are at least two ways to run firebird. superserver has been choosen
+# however there are no strong reasons to prefer this or the other one AFAIK
+# Eg superserver is said to be most efficiently using resources according to
+# http://www.firebirdsql.org/manual/qsg25-classic-or-super.html
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.firebird;
+
+  firebird = cfg.package;
+
+  pidFile = "${cfg.pidDir}/firebirdd.pid";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.firebird = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the firebird super server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.firebirdSuper;
+        /*
+          Example: <code>package = pkgs.firebirdSuper.override { icu =
+            pkgs.icu; };</code> which is not recommended for compatibility
+            reasons. See comments at the firebirdSuper derivation
+        */
+
+        description = "
+          Which firebird derivation to use.
+        ";
+      };
+
+      port = mkOption {
+        default = "3050";
+        description = "Port of Firebird.";
+      };
+
+      user = mkOption {
+        default = "firebird";
+        description = "User account under which firebird runs.";
+      };
+
+      dataDir = mkOption {
+        default = "/var/db/firebird/data"; # ubuntu is using /var/lib/firebird/2.1/data/.. ?
+        description = "Location where firebird databases are stored.";
+      };
+
+      pidDir = mkOption {
+        default = "/run/firebird";
+        description = "Location of the file which stores the PID of the firebird server.";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.firebird.enable {
+
+    users.extraUsers.firebird.description =  "Firebird server user";
+
+    environment.systemPackages = [firebird];
+
+    systemd.services.firebird =
+      { description = "firebird super server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        # TODO: moving security2.fdb into the data directory works, maybe there
+        # is a better way
+        preStart =
+          ''
+            secureDir="${cfg.dataDir}/../system"
+
+            mkdir -m 0700 -p \
+              "${cfg.dataDir}" \
+              "${cfg.pidDir}" \
+              /var/log/firebird \
+              "$secureDir"
+
+            if ! test -e "$secureDir/security2.fdb"; then
+                cp ${firebird}/security2.fdb "$secureDir"
+            fi
+
+            chown -R ${cfg.user} "${cfg.pidDir}" "${cfg.dataDir}" "$secureDir" /var/log/firebird
+            chmod -R 700 "${cfg.pidDir}" "${cfg.dataDir}" "$secureDir" /var/log/firebird
+          '';
+
+        serviceConfig.PermissionsStartOnly = true; # preStart must be run as root
+        serviceConfig.User = cfg.user;
+        serviceConfig.ExecStart = ''${firebird}/bin/fbserver -d'';
+
+        # TODO think about shutdown
+      };
+
+    environment.etc."firebird/firebird.msg".source = "${firebird}/firebird.msg";
+
+    # think about this again - and eventually make it an option
+    environment.etc."firebird/firebird.conf".text = ''
+      # RootDirectory = Restrict ${cfg.dataDir}
+      DatabaseAccess = Restrict ${cfg.dataDir}
+      ExternalFileAccess = Restrict ${cfg.dataDir}
+      # what is this? is None allowed?
+      UdfAccess = None
+      # "Native" =  traditional interbase/firebird, "mixed" is windows only
+      Authentication = Native
+
+      # defaults to -1 on non Win32
+      #MaxUnflushedWrites = 100
+      #MaxUnflushedWriteTime = 100
+
+      # show trace if trouble occurs (does this require debug build?)
+      # BugcheckAbort = 0
+      # ConnectionTimeout = 180
+
+      #RemoteServiceName = gds_db
+      RemoteServicePort = ${cfg.port}
+
+      # randomly choose port for server Event Notification
+      #RemoteAuxPort = 0
+      # rsetrict connections to a network card:
+      #RemoteBindAddress =
+      # there are some more settings ..
+    '';
+    };
+
+}
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
new file mode 100644
index 00000000000..a0e264f2299
--- /dev/null
+++ b/nixos/modules/services/databases/memcached.nix
@@ -0,0 +1,97 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.memcached;
+
+  memcached = pkgs.memcached;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.memcached = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable Memcached.
+        ";
+      };
+
+      user = mkOption {
+        default = "memcached";
+        description = "The user to run Memcached as";
+      };
+
+      listen = mkOption {
+        default = "127.0.0.1";
+        description = "The IP address to bind to";
+      };
+
+      port = mkOption {
+        default = 11211;
+        description = "The port to bind to";
+      };
+
+      socket = mkOption {
+        default = "";
+        description = "Unix socket path to listen on. Setting this will disable network support";
+        example = "/var/run/memcached";
+      };
+
+      maxMemory = mkOption {
+        default = 64;
+        description = "The maximum amount of memory to use for storage, in megabytes.";
+      };
+
+      maxConnections = mkOption {
+        default = 1024;
+        description = "The maximum number of simultaneous connections";
+      };
+
+      extraOptions = mkOption {
+        default = [];
+        description = "A list of extra options that will be added as a suffix when running memcached";
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.memcached.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "Memcached server user";
+      };
+
+    environment.systemPackages = [ memcached ];
+
+    systemd.services.memcached =
+      { description = "Memcached server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart =
+            let
+              networking = if cfg.socket != ""
+                then "-s ${cfg.socket}"
+                else "-l ${cfg.listen} -p ${toString cfg.port}";
+            in "${memcached}/bin/memcached ${networking} -m ${toString cfg.maxMemory} -c ${toString cfg.maxConnections} ${concatStringsSep " " cfg.extraOptions}";
+
+          User = cfg.user;
+        };
+      };
+  };
+
+}
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
new file mode 100644
index 00000000000..d6299415893
--- /dev/null
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -0,0 +1,130 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  b2s = x: if x then "true" else "false";
+
+  cfg = config.services.mongodb;
+
+  mongodb = cfg.package;
+
+  mongoCnf = pkgs.writeText "mongodb.conf"
+  ''
+    bind_ip = ${cfg.bind_ip}
+    ${optionalString cfg.quiet "quiet = true"}
+    dbpath = ${cfg.dbpath}
+    logpath = ${cfg.logpath}
+    logappend = ${b2s cfg.logappend}
+    ${optionalString (cfg.replSetName != "") "replSet = ${cfg.replSetName}"}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mongodb = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the MongoDB server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.mongodb;
+        description = "
+          Which MongoDB derivation to use.
+        ";
+      };
+
+      user = mkOption {
+        default = "mongodb";
+        description = "User account under which MongoDB runs";
+      };
+
+      bind_ip = mkOption {
+        default = "127.0.0.1";
+        description = "IP to bind to";
+      };
+
+      quiet = mkOption {
+        default = false;
+        description = "quieter output";
+      };
+
+      dbpath = mkOption {
+        default = "/var/db/mongodb";
+        description = "Location where MongoDB stores its files";
+      };
+
+      logpath = mkOption {
+        default = "/var/log/mongodb/mongod.log";
+        description = "Location where MongoDB stores its logfile";
+      };
+
+      logappend = mkOption {
+        default = true;
+        description = "Append logfile instead over overwriting";
+      };
+
+      replSetName = mkOption {
+        default = "";
+        description = ''
+          If this instance is part of a replica set, set its name here.
+          Otherwise, leave empty to run as single node.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.mongodb.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "MongoDB server user";
+      };
+
+    environment.systemPackages = [ mongodb ];
+
+    systemd.services.mongodb_init =
+      { description = "MongoDB server initialisation";
+
+        wantedBy = [ "mongodb.service" ];
+        before = [ "mongodb.service" ];
+
+        serviceConfig.Type = "oneshot";
+
+        script = ''
+          if ! test -e ${cfg.dbpath}; then
+              install -d -m0700 -o ${cfg.user} ${cfg.dbpath}
+              install -d -m0755 -o ${cfg.user} `dirname ${cfg.logpath}`
+          fi
+        '';
+      };
+
+    systemd.services.mongodb =
+      { description = "MongoDB server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart = "${mongodb}/bin/mongod --quiet --config ${mongoCnf}";
+          User = cfg.user;
+        };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
new file mode 100644
index 00000000000..663c2cc4505
--- /dev/null
+++ b/nixos/modules/services/databases/mysql.nix
@@ -0,0 +1,245 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.mysql;
+
+  mysql = cfg.package;
+
+  pidFile = "${cfg.pidDir}/mysqld.pid";
+
+  mysqldOptions =
+    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
+    "--pid-file=${pidFile}";
+
+  myCnf = pkgs.writeText "my.cnf"
+  ''
+    [mysqld]
+    ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
+    ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
+    ${optionalString (cfg.replication.role == "slave")
+    ''
+      master-host = ${cfg.replication.masterHost}
+      master-user = ${cfg.replication.masterUser}
+      master-password = ${cfg.replication.masterPassword}
+      master-port = ${toString cfg.replication.masterPort}
+    ''}
+    ${cfg.extraOptions}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mysql = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the MySQL server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.mysql;
+        description = "
+          Which MySQL derivation to use.
+        ";
+      };
+
+      port = mkOption {
+        default = "3306";
+        description = "Port of MySQL";
+      };
+
+      user = mkOption {
+        default = "mysql";
+        description = "User account under which MySQL runs";
+      };
+
+      dataDir = mkOption {
+        default = "/var/mysql"; # !!! should be /var/db/mysql
+        description = "Location where MySQL stores its table files";
+      };
+
+      pidDir = mkOption {
+        default = "/var/run/mysql";
+        description = "Location of the file which stores the PID of the MySQL server";
+      };
+
+      extraOptions = mkOption {
+        default = "";
+        example = ''
+          key_buffer_size = 6G
+          table_cache = 1600
+          log-error = /var/log/mysql_err.log
+        '';
+        description = ''
+          Provide extra options to the MySQL configuration file.
+
+          Please note, that these options are added to the
+          <literal>[mysqld]</literal> section so you don't need to explicitly
+          state it again.
+        '';
+      };
+
+      initialDatabases = mkOption {
+        default = [];
+        description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL";
+        example = [
+          { name = "foodatabase"; schema = ./foodatabase.sql; }
+          { name = "bardatabase"; schema = ./bardatabase.sql; }
+        ];
+      };
+
+      initialScript = mkOption {
+        default = null;
+        description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database";
+      };
+
+      # FIXME: remove this option; it's a really bad idea.
+      rootPassword = mkOption {
+        default = null;
+        description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty.";
+      };
+
+      replication = {
+        role = mkOption {
+          default = "none";
+          description = "Role of the MySQL server instance. Can be either: master, slave or none";
+        };
+
+        serverId = mkOption {
+          default = 1;
+          description = "Id of the MySQL server instance. This number must be unique for each instance";
+        };
+
+        masterHost = mkOption {
+          description = "Hostname of the MySQL master server";
+        };
+
+        masterUser = mkOption {
+          description = "Username of the MySQL replication user";
+        };
+
+        masterPassword = mkOption {
+          description = "Password of the MySQL replication user";
+        };
+
+        masterPort = mkOption {
+          default = 3306;
+          description = "Port number on which the MySQL master server runs";
+        };
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.mysql.enable {
+
+    users.extraUsers.mysql = {
+      description = "MySQL server user";
+      group = "mysql";
+      uid = config.ids.uids.mysql;
+    };
+
+    users.extraGroups.mysql.gid = config.ids.gids.mysql;
+
+    environment.systemPackages = [mysql];
+
+    systemd.services.mysql =
+      { description = "MySQL Server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        unitConfig.RequiresMountsFor = "${cfg.dataDir}";
+
+        preStart =
+          ''
+            if ! test -e ${cfg.dataDir}/mysql; then
+                mkdir -m 0700 -p ${cfg.dataDir}
+                chown -R ${cfg.user} ${cfg.dataDir}
+                ${mysql}/bin/mysql_install_db ${mysqldOptions}
+                touch /tmp/mysql_init
+            fi
+
+            mkdir -m 0700 -p ${cfg.pidDir}
+            chown -R ${cfg.user} ${cfg.pidDir}
+          '';
+
+        serviceConfig.ExecStart = "${mysql}/libexec/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
+
+        postStart =
+          ''
+            # Wait until the MySQL server is available for use
+            count=0
+            while [ ! -e /tmp/mysql.sock ]
+            do
+                if [ $count -eq 30 ]
+                then
+                    echo "Tried 30 times, giving up..."
+                    exit 1
+                fi
+
+                echo "MySQL daemon not yet started. Waiting for 1 second..."
+                count=$((count++))
+                sleep 1
+            done
+
+            if [ -f /tmp/mysql_init ]
+            then
+                ${concatMapStrings (database:
+                  ''
+                    # Create initial databases
+                    if ! test -e "${cfg.dataDir}/${database.name}"; then
+                        echo "Creating initial database: ${database.name}"
+                        ( echo "create database ${database.name};"
+                          echo "use ${database.name};"
+
+                          if [ -f "${database.schema}" ]
+                          then
+                              cat ${database.schema}
+                          elif [ -d "${database.schema}" ]
+                          then
+                              cat ${database.schema}/mysql-databases/*.sql
+                          fi
+                        ) | ${mysql}/bin/mysql -u root -N
+                    fi
+                  '') cfg.initialDatabases}
+
+                ${optionalString (cfg.initialScript != null)
+                  ''
+                    # Execute initial script
+                    cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N
+                  ''}
+
+                ${optionalString (cfg.rootPassword != null)
+                  ''
+                    # Change root password
+
+                    ( echo "use mysql;"
+                      echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';"
+                      echo "flush privileges;"
+                    ) | ${mysql}/bin/mysql -u root -N
+                  ''}
+
+              rm /tmp/mysql_init
+            fi
+          ''; # */
+
+        serviceConfig.ExecStop =
+          "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/mysql55.nix b/nixos/modules/services/databases/mysql55.nix
new file mode 100644
index 00000000000..46148d68f4c
--- /dev/null
+++ b/nixos/modules/services/databases/mysql55.nix
@@ -0,0 +1,248 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.mysql55;
+
+  mysql = cfg.package;
+
+  pidFile = "${cfg.pidDir}/mysqld.pid";
+
+  mysqldOptions =
+    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
+    "--pid-file=${pidFile}";
+
+  myCnf = pkgs.writeText "my.cnf"
+  ''
+    [mysqld]
+    ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
+    ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
+    ${cfg.extraOptions}
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.mysql55 = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the MySQL server.
+        ";
+      };
+
+      package = mkOption {
+        default = pkgs.mysql55;
+        description = "
+          Which MySQL derivation to use.
+        ";
+      };
+
+      port = mkOption {
+        default = "3306";
+        description = "Port of MySQL";
+      };
+
+      user = mkOption {
+        default = "mysql";
+        description = "User account under which MySQL runs";
+      };
+
+      dataDir = mkOption {
+        default = "/var/mysql"; # !!! should be /var/db/mysql
+        description = "Location where MySQL stores its table files";
+      };
+
+      pidDir = mkOption {
+        default = "/var/run/mysql";
+        description = "Location of the file which stores the PID of the MySQL server";
+      };
+
+      extraOptions = mkOption {
+        default = "";
+        example = ''
+          key_buffer_size = 6G
+          table_cache = 1600
+          log-error = /var/log/mysql_err.log
+        '';
+        description = ''
+          Provide extra options to the MySQL configuration file.
+
+          Please note, that these options are added to the
+          <literal>[mysqld]</literal> section so you don't need to explicitly
+          state it again.
+        '';
+      };
+
+      initialDatabases = mkOption {
+        default = [];
+        description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL";
+        example = [
+          { name = "foodatabase"; schema = ./foodatabase.sql; }
+          { name = "bardatabase"; schema = ./bardatabase.sql; }
+        ];
+      };
+
+      initialScript = mkOption {
+        default = null;
+        description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database";
+      };
+
+      # FIXME: remove this option; it's a really bad idea.
+      rootPassword = mkOption {
+        default = null;
+        description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty.";
+      };
+
+      replication = {
+        role = mkOption {
+          default = "none";
+          description = "Role of the MySQL server instance. Can be either: master, slave or none";
+        };
+
+        serverId = mkOption {
+          default = 1;
+          description = "Id of the MySQL server instance. This number must be unique for each instance";
+        };
+
+        masterHost = mkOption {
+          description = "Hostname of the MySQL master server";
+        };
+
+        masterUser = mkOption {
+          description = "Username of the MySQL replication user";
+        };
+
+        masterPassword = mkOption {
+          description = "Password of the MySQL replication user";
+        };
+
+        masterPort = mkOption {
+          default = 3306;
+          description = "Port number on which the MySQL master server runs";
+        };
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.mysql55.enable {
+
+    users.extraUsers.mysql = {
+      description = "MySQL server user";
+      group = "mysql";
+      uid = config.ids.uids.mysql;
+    };
+
+    users.extraGroups.mysql.gid = config.ids.gids.mysql;
+
+    environment.systemPackages = [mysql];
+
+    systemd.services.mysql =
+      { description = "MySQL Server";
+
+        wantedBy = [ "multi-user.target" ];
+
+        unitConfig.RequiresMountsFor = "${cfg.dataDir}";
+
+        preStart =
+          ''
+            if ! test -e ${cfg.dataDir}/mysql; then
+                mkdir -m 0700 -p ${cfg.dataDir}
+                chown -R ${cfg.user} ${cfg.dataDir}
+                ${mysql}/bin/mysql_install_db ${mysqldOptions}
+                touch /tmp/mysql_init
+            fi
+
+            mkdir -m 0700 -p ${cfg.pidDir}
+            chown -R ${cfg.user} ${cfg.pidDir}
+          '';
+
+        serviceConfig.ExecStart = "${mysql}/bin/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
+
+        postStart =
+          ''
+            # Wait until the MySQL server is available for use
+            count=0
+            while [ ! -e /tmp/mysql.sock ]
+            do
+                if [ $count -eq 30 ]
+                then
+                    echo "Tried 30 times, giving up..."
+                    exit 1
+                fi
+
+                echo "MySQL daemon not yet started. Waiting for 1 second..."
+                count=$((count++))
+                sleep 1
+            done
+
+            if [ -f /tmp/mysql_init ]
+            then
+                ${concatMapStrings (database:
+                  ''
+                    # Create initial databases
+                    if ! test -e "${cfg.dataDir}/${database.name}"; then
+                        echo "Creating initial database: ${database.name}"
+                        ( echo "create database ${database.name};"
+                          echo "use ${database.name};"
+
+                          if [ -f "${database.schema}" ]
+                          then
+                              cat ${database.schema}
+                          elif [ -d "${database.schema}" ]
+                          then
+                              cat ${database.schema}/mysql-databases/*.sql
+                          fi
+                        ) | ${mysql}/bin/mysql -u root -N
+                    fi
+                  '') cfg.initialDatabases}
+                
+                ${optionalString (cfg.replication.role == "slave")
+                  ''
+                    # Set up the replication master
+                    
+                    ( echo "stop slave;"
+                      echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
+                      echo "start slave;"
+                    ) | ${mysql}/bin/mysql -u root -N
+                  ''}
+
+                ${optionalString (cfg.initialScript != null)
+                  ''
+                    # Execute initial script
+                    cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N
+                  ''}
+
+                ${optionalString (cfg.rootPassword != null)
+                  ''
+                    # Change root password
+
+                    ( echo "use mysql;"
+                      echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';"
+                      echo "flush privileges;"
+                    ) | ${mysql}/bin/mysql -u root -N
+                  ''}
+
+              rm /tmp/mysql_init
+            fi
+          ''; # */
+
+        serviceConfig.ExecStop =
+          "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
new file mode 100644
index 00000000000..a4dd30be1fb
--- /dev/null
+++ b/nixos/modules/services/databases/openldap.nix
@@ -0,0 +1,58 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.openldap;
+  openldap = pkgs.openldap;
+
+  configFile = pkgs.writeText "slapd.conf" cfg.extraConfig;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.openldap = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the ldap server.
+        ";
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "
+          sldapd.conf configuration
+        ";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.openldap.enable {
+
+    environment.systemPackages = [ openldap ];
+
+    systemd.services.openldap = {
+      description = "LDAP server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      preStart = ''
+        mkdir -p /var/run/slapd
+      '';
+      serviceConfig.ExecStart = "${openldap}/libexec/slapd -d 0 -f ${configFile}";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
new file mode 100644
index 00000000000..fc6b5b167b8
--- /dev/null
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -0,0 +1,232 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.postgresql;
+
+  # see description of extraPlugins
+  postgresqlAndPlugins = pg:
+    if cfg.extraPlugins == [] then pg
+    else pkgs.buildEnv {
+      name = "postgresql-and-plugins-${(builtins.parseDrvName pg.name).version}";
+      paths = [ pg ] ++ cfg.extraPlugins;
+      postBuild =
+        ''
+          mkdir -p $out/bin
+          rm $out/bin/{pg_config,postgres,pg_ctl}
+          cp --target-directory=$out/bin ${pg}/bin/{postgres,pg_config,pg_ctl}
+        '';
+    };
+
+  postgresql = postgresqlAndPlugins cfg.package;
+
+  flags = optional cfg.enableTCPIP "-i";
+
+  # The main PostgreSQL configuration file.
+  configFile = pkgs.writeText "postgresql.conf"
+    ''
+      hba_file = '${pkgs.writeText "pg_hba.conf" cfg.authentication}'
+      ident_file = '${pkgs.writeText "pg_ident.conf" cfg.identMap}'
+      log_destination = 'stderr'
+      ${cfg.extraConfig}
+    '';
+
+  pre84 = versionOlder (builtins.parseDrvName postgresql.name).version "8.4";
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.postgresql = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Whether to run PostgreSQL.
+        '';
+      };
+
+      package = mkOption {
+        example = literalExample "pkgs.postgresql92";
+        description = ''
+          PostgreSQL package to use.
+        '';
+      };
+
+      port = mkOption {
+        default = "5432";
+        description = ''
+          Port for PostgreSQL.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = "/var/db/postgresql";
+        description = ''
+          Data directory for PostgreSQL.
+        '';
+      };
+
+      authentication = mkOption {
+        default = "";
+        description = ''
+          Defines how users authenticate themselves to the server.
+        '';
+      };
+
+      identMap = mkOption {
+        default = "";
+        description = ''
+          Defines the mapping from system users to database users.
+        '';
+      };
+
+      initialScript = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          A file containing SQL statements to execute on first startup.
+        '';
+      };
+
+      authMethod = mkOption {
+        default = " ident sameuser ";
+        description = ''
+          How to authorize users.
+          Note: ident needs absolute trust to all allowed client hosts.
+        '';
+      };
+
+      enableTCPIP = mkOption {
+        default = false;
+        description = ''
+          Whether to run PostgreSQL with -i flag to enable TCP/IP connections.
+        '';
+      };
+
+      extraPlugins = mkOption {
+        default = [];
+        example = "pkgs.postgis"; # of course don't use a string here!
+        description = ''
+          When this list contains elements a new store path is created.
+          PostgreSQL and the elments are symlinked into it. Then pg_config,
+          postgres and pc_ctl are copied to make them use the new
+          $out/lib directory as pkglibdir. This makes it possible to use postgis
+          without patching the .sql files which reference $libdir/postgis-1.5.
+        '';
+        # Note: the duplication of executables is about 4MB size.
+        # So a nicer solution was patching postgresql to allow setting the
+        # libdir explicitely.
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "Additional text to be appended to <filename>postgresql.conf</filename>.";
+      };
+
+      recoveryConfig = mkOption {
+        default = null;
+        type = types.nullOr types.string;
+        description = ''
+          Values to put into recovery.conf file.
+        '';
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.postgresql.enable {
+
+    services.postgresql.authentication =
+      ''
+        # Generated file; do not edit!
+        local all all              ident ${optionalString pre84 "sameuser"}
+        host  all all 127.0.0.1/32 md5
+        host  all all ::1/128      md5
+      '';
+
+    users.extraUsers.postgres =
+      { name = "postgres";
+        uid = config.ids.uids.postgres;
+        group = "postgres";
+        description = "PostgreSQL server user";
+      };
+
+    users.extraGroups.postgres.gid = config.ids.gids.postgres;
+
+    environment.systemPackages = [postgresql];
+
+    systemd.services.postgresql =
+      { description = "PostgreSQL Server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        environment.PGDATA = cfg.dataDir;
+
+        path = [ pkgs.su postgresql ];
+
+        preStart =
+          ''
+            # Initialise the database.
+            if ! test -e ${cfg.dataDir}; then
+                mkdir -m 0700 -p ${cfg.dataDir}
+                chown -R postgres ${cfg.dataDir}
+                su -s ${pkgs.stdenv.shell} postgres -c 'initdb -U root'
+                rm -f ${cfg.dataDir}/*.conf
+                touch "${cfg.dataDir}/.first_startup"
+            fi
+
+            ln -sfn "${configFile}" "${cfg.dataDir}/postgresql.conf"
+            ${optionalString (cfg.recoveryConfig != null) ''
+              ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \
+                "${cfg.dataDir}/recovery.conf"
+            ''}
+          ''; # */
+
+        serviceConfig =
+          { ExecStart = "@${postgresql}/bin/postgres postgres ${toString flags}";
+            User = "postgres";
+            Group = "postgres";
+            PermissionsStartOnly = true;
+
+            # Shut down Postgres using SIGINT ("Fast Shutdown mode").  See
+            # http://www.postgresql.org/docs/current/static/server-shutdown.html
+            KillSignal = "SIGINT";
+
+            # Give Postgres a decent amount of time to clean up after
+            # receiving systemd's SIGINT.
+            TimeoutSec = 120;
+          };
+
+        # Wait for PostgreSQL to be ready to accept connections.
+        postStart =
+          ''
+            while ! psql postgres -c "" 2> /dev/null; do
+                if ! kill -0 "$MAINPID"; then exit 1; fi
+                sleep 0.1
+            done
+
+            if test -e "${cfg.dataDir}/.first_startup"; then
+              ${optionalString (cfg.initialScript != null) ''
+                cat "${cfg.initialScript}" | psql postgres
+              ''}
+              rm -f "${cfg.dataDir}/.first_startup"
+            fi
+          '';
+
+        unitConfig.RequiresMountsFor = "${cfg.dataDir}";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
new file mode 100644
index 00000000000..5bc58c73bd6
--- /dev/null
+++ b/nixos/modules/services/databases/redis.nix
@@ -0,0 +1,216 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.redis;
+  redisBool = b: if b then "yes" else "no";
+  condOption = name: value: if value != null then "${name} ${toString value}" else "";
+
+  redisConfig = pkgs.writeText "redis.conf" ''
+    pidfile ${cfg.pidFile}
+    port ${toString cfg.port}
+    ${condOption "bind" cfg.bind}
+    ${condOption "unixsocket" cfg.unixSocket}
+    loglevel ${cfg.logLevel}
+    logfile ${cfg.logfile}
+    databases ${toString cfg.databases}
+    ${concatMapStrings (d: "save ${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}\n") cfg.save}
+    dbfilename ${cfg.dbFilename}
+    dir ${toString cfg.dbpath}
+    ${if cfg.slaveOf != null then "slaveof ${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}" else ""}
+    ${condOption "masterauth" cfg.masterAuth}
+    ${condOption "requirepass" cfg.requirePass}
+    appendOnly ${redisBool cfg.appendOnly}
+    appendfsync ${cfg.appendFsync}
+    slowlog-log-slower-than ${toString cfg.slowLogLogSlowerThan}
+    slowlog-max-len ${toString cfg.slowLogMaxLen}
+    ${cfg.extraConfig}
+  '';
+in
+{
+
+  ###### interface
+
+  options = {
+
+    services.redis = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable the Redis server.";
+      };
+
+      package = mkOption {
+        default = pkgs.redis;
+        description = "Which Redis derivation to use.";
+      };
+
+      user = mkOption {
+        default = "redis";
+        description = "User account under which Redis runs";
+      };
+
+      pidFile = mkOption {
+        default = "/var/lib/redis/redis.pid";
+        description = "";
+      };
+
+      port = mkOption {
+        default = 6379;
+        description = "The port for Redis to listen to";
+        type = with types; int;
+      };
+
+      bind = mkOption {
+        default = null; # All interfaces
+        description = "The IP interface to bind to";
+        example = "127.0.0.1";
+      };
+
+      unixSocket = mkOption {
+        default = null;
+        description = "The path to the socket to bind to";
+        example = "/var/run/redis.sock";
+      };
+
+      logLevel = mkOption {
+        default = "notice"; # debug, verbose, notice, warning
+        example = "debug";
+        description = "Specify the server verbosity level, options: debug, verbose, notice, warning";
+        type = with types; string;
+      };
+
+      logfile = mkOption {
+        default = "stdout";
+        description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
+        example = "/var/log/redis.log";
+        type = with types; string;
+      };
+
+      databases = mkOption {
+        default = 16;
+        description = "Set the number of databases.";
+        type = with types; int;
+      };
+
+      save = mkOption {
+        default = [ [900 1] [300 10] [60 10000] ];
+        description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
+        example = [ [900 1] [300 10] [60 10000] ];
+      };
+
+      dbFilename = mkOption {
+        default = "dump.rdb";
+        description = "The filename where to dump the DB";
+        type = with types; string;
+      };
+
+      dbpath = mkOption {
+        default = "/var/lib/redis";
+        description = "The DB will be written inside this directory, with the filename specified using the 'dbFilename' configuration";
+        type = with types; string;
+      };
+
+      slaveOf = mkOption {
+        default = null; # { ip, port }
+        description = "An attribute set with two attributes: ip and port to which this redis instance acts as a slave";
+        example = { ip = "192.168.1.100"; port = 6379; };
+      };
+
+      masterAuth = mkOption {
+        default = null;
+        description = ''If the master is password protected (using the requirePass configuration)
+        it is possible to tell the slave to authenticate before starting the replication synchronization
+        process, otherwise the master will refuse the slave request.
+        (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
+      };
+
+      requirePass = mkOption {
+        default = null;
+        description = "Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)";
+        example = "letmein!";
+      };
+
+      appendOnly = mkOption {
+        default = false;
+        description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
+        type = with types; bool;
+      };
+
+      appendOnlyFilename = mkOption {
+        default = "appendonly.aof";
+        description = "Filename for the append-only file (stored inside of dbpath)";
+        type = with types; string;
+      };
+
+      appendFsync = mkOption {
+        default = "everysec"; # no, always, everysec
+        description = "How often to fsync the append-only log, options: no, always, everysec";
+        type = with types; string;
+      };
+
+      slowLogLogSlowerThan = mkOption {
+        default = 10000;
+        description = "Log queries whose execution take longer than X in milliseconds";
+        example = 1000;
+        type = with types; int;
+      };
+
+      slowLogMaxLen = mkOption {
+        default = 128;
+        description = "Maximum number of items to keep in slow log";
+        type = with types; int;
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        description = "Extra configuration options for redis.conf";
+        type = with types; string;
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.redis.enable {
+
+    users.extraUsers = singleton
+      { name = cfg.user;
+        description = "Redis database user";
+      };
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.redis_init =
+      { description = "Redis server initialisation";
+
+        wantedBy = [ "redis.service" ];
+        before = [ "redis.service" ];
+
+        serviceConfig.Type = "oneshot";
+
+        script = ''
+          if ! test -e ${cfg.dbpath}; then
+              install -d -m0700 -o ${cfg.user} ${cfg.dbpath}
+          fi
+        '';
+      };
+
+    systemd.services.redis =
+      { description = "Redis server";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          ExecStart = "${cfg.package}/bin/redis-server ${redisConfig}";
+          User = cfg.user;
+        };
+      };
+
+  };
+
+}
diff --git a/nixos/modules/services/databases/virtuoso.nix b/nixos/modules/services/databases/virtuoso.nix
new file mode 100644
index 00000000000..6a29fc13211
--- /dev/null
+++ b/nixos/modules/services/databases/virtuoso.nix
@@ -0,0 +1,98 @@
+{ config, pkgs, ... }:
+let
+  cfg = config.services.virtuoso;
+  virtuosoUser = "virtuoso";
+  stateDir = "/var/lib/virtuoso";
+in
+with pkgs.lib;
+{
+
+  ###### interface
+
+  options = {
+
+    services.virtuoso = {
+
+      enable = mkOption {
+        default = false;
+        description = "Whether to enable Virtuoso Opensource database server.";
+      };
+
+      config = mkOption {
+        default = "";
+        description = "Extra options to put into Virtuoso configuration file.";
+      };
+
+      parameters = mkOption {
+        default = "";
+        description = "Extra options to put into [Parameters] section of Virtuoso configuration file.";
+      };
+
+      listenAddress = mkOption {
+	default = "1111";
+	example = "myserver:1323";
+        description = "ip:port or port to listen on.";
+      };
+
+      httpListenAddress = mkOption {
+	default = null;
+	example = "myserver:8080";
+        description = "ip:port or port for Virtuoso HTTP server to listen on.";
+      };
+
+      dirsAllowed = mkOption {
+	default = null;
+	example = "/www, /home/";
+        description = "A list of directories Virtuoso is allowed to access";
+      };
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    users.extraUsers = singleton
+      { name = virtuosoUser;
+        uid = config.ids.uids.virtuoso;
+        description = "virtuoso user";
+        home = stateDir;
+      };
+
+    jobs.virtuoso = {
+      name = "virtuoso";
+      startOn = "filesystem";
+
+      preStart = ''
+	mkdir -p ${stateDir}
+	chown ${virtuosoUser} ${stateDir}
+      '';
+
+      script = ''
+	cd ${stateDir}
+	${pkgs.virtuoso}/bin/virtuoso-t +foreground +configfile ${pkgs.writeText "virtuoso.ini" cfg.config}
+      '';
+    };
+
+    services.virtuoso.config = ''
+      [Database]
+      DatabaseFile=${stateDir}/x-virtuoso.db
+      TransactionFile=${stateDir}/x-virtuoso.trx
+      ErrorLogFile=${stateDir}/x-virtuoso.log
+      xa_persistent_file=${stateDir}/x-virtuoso.pxa
+
+      [Parameters]
+      ServerPort=${cfg.listenAddress}
+      RunAs=${virtuosoUser}
+      ${optionalString (cfg.dirsAllowed != null) "DirsAllowed=${cfg.dirsAllowed}"}
+      ${cfg.parameters}
+
+      [HTTPServer]
+      ${optionalString (cfg.httpListenAddress != null) "ServerPort=${cfg.httpListenAddress}"}
+    '';
+
+  };
+
+}