diff --git a/type/__nsswitch/explorer/sources b/type/__nsswitch/explorer/sources
new file mode 100755
index 000000000..7aa71d60e
--- /dev/null
+++ b/type/__nsswitch/explorer/sources
@@ -0,0 +1,52 @@
+#!/bin/sh -e
+#
+# 2023,2025 Dennis Camera (dennis.camera at riiengineering.ch)
+#
+# This file is part of skonfig-base.
+#
+# skonfig-base is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# skonfig-base is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with skonfig-base. If not, see .
+#
+# Prints the sources currently configured for the database.
+#
+
+test -f /etc/nsswitch.conf || exit 0
+
+awk -v db_name="${__object_id:?}" '
+# prepare line
+{
+ # remove comments
+ sub(/[ \\t]*#.*$/, "")
+ # merge line continuations to single line (line continuations do not apply
+ # to comments, btw).
+ while (/\\$/) {
+ sub(/\\$/, (0 < (getline _nl) ? _nl : ""))
+ }
+}
+
+$1 == db_name ":" {
+ # store found line, because in nsswitch.conf(5) the last match wins
+ line = $0
+}
+
+END {
+ if (line) {
+ $0 = line
+
+ for (i = 2; i <= NF; ++i) {
+ printf "%s%s", (i>2 ? " " : ""), $i
+ }
+ printf ORS
+ }
+}
+' /etc/nsswitch.conf
diff --git a/type/__nsswitch/gencode-remote b/type/__nsswitch/gencode-remote
new file mode 100755
index 000000000..357469ade
--- /dev/null
+++ b/type/__nsswitch/gencode-remote
@@ -0,0 +1,157 @@
+#!/bin/sh -e
+#
+# 2023,2025 Dennis Camera (dennis.camera at riiengineering.ch)
+#
+# This file is part of skonfig-base.
+#
+# skonfig-base is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# skonfig-base is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with skonfig-base. If not, see .
+#
+
+join_lines() {
+ test -r "$2" || return 0
+ awk -v sep="$1" 'NR>1{printf "%s",sep}{printf "%s",$0}END{printf ORS}' "$2"
+}
+
+if
+ join_lines ' ' "${__object:?}/parameter/source" \
+ | cmp -s "${__object:?}/explorer/sources" -
+then
+ # no changes needed
+ exit 0
+fi
+
+os=$(cat "${__global:?}/explorer/os")
+case ${os}
+in
+ (fedora|redhat|centos|almalinux|rocky|oraclelinux|scientific|amazon)
+ echo 'This type currently does not work on Red Hat, because it does not support authconfig/authselect.' >&2
+ echo 'Please contribute an implementation for it if you can.' >&2
+ exit 1
+ ;;
+ (solaris)
+ case $(cat "${__global:?}/explorer/os_version")
+ in
+ (5.11|5.11.*|'')
+ # Solaris 11 manages name service switch information in SMF
+ # https://docs.oracle.com/cd/E23824_01/html/821-1455/c8switch-18904.html
+ echo 'This type currently does not work on Solaris 11, because it does not support managing the information in SMF.' >&2
+ echo 'Please contribute an implementation for it if you can.' >&2
+ exit 1
+ ;;
+ esac
+ ;;
+esac
+
+if ! test -s "${__object:?}/explorer/sources"
+then
+ # possibly the file does not exist
+ cat <<'EOF'
+test -f /etc/nsswitch.conf || (
+ umask 0022
+ :>/etc/nsswitch.conf
+ chown 0:0 /etc/nsswitch.conf
+)
+
+EOF
+fi
+
+# update nsswitch.conf(5)
+
+# NOTE: the updated content is cat(1) into /etc/nsswitch.conf to achieve the
+# correct behaviour if /etc/nsswitch.conf is a symbolic link.
+cat <<'EOF'
+awk -v db_name="${__object_id:?}" -v source_file="${__object:?}/parameter/source" '
+function update_indents( i, _l) {
+ _l = $0
+ while (_l ~ /[\t]/) {
+ # expand tab stops
+ i = index(_l, "\t")-1
+ _l = substr(_l, 1, i) sprintf("%"(TAB_WIDTH - (i % TAB_WIDTH))"s", "") substr(_l, i+2)
+ }
+
+ i = index(_l, $1) - 1
+ i += length($1)
+ i += index(substr(_l, i+1), $2) - 1
+
+ if (/[\t]/ || i > (index(_l, $1) + length($1))) {
+ # only remember indentation if there are at least two spaces or a tab
+ indents[i]++
+ num_indents++
+ num_indents_tabs += /[\t]/
+ }
+}
+function fmt_db(db, cnt, cnt_total, il, i) {
+ # set initial cnt = 2 to ignore indentations which only occur once
+ il = (length(db) + 1); cnt = 2
+
+ for (i in indents) {
+ cnt_total += indents[i]
+ if (indents[i] > cnt) { il = i; cnt = indents[i] }
+ }
+
+ if ((cnt * 2) <= cnt_total) {
+ # no clear indentation style (< 50%), ignore
+ return db " "
+ }
+
+ if ((2 * num_indents_tabs) > num_indents) {
+ # this file uses tabs predominantly
+ cnt = (il - length(db)) / TAB_WIDTH
+ for (i = 0; i < cnt; ++i) { db = db "\t" }
+ return db
+ } else {
+ return sprintf("%-"il"s", db)
+ }
+}
+
+BEGIN {
+ TAB_WIDTH = 8
+
+ while (0 < (getline src < source_file)) {
+ sources = sources (sources ? " " : "") src
+ }
+ close(source_file)
+}
+
+$1 ~ /:$/ {
+ # looks like an entry
+ update_indents()
+
+ if ($1 == db_name":") {
+ # only replace the first occurrence
+ if (!f && sources) {
+ match($0, /:[ \t]*/)
+ printf "%s%s" ORS, substr($0, 1, RSTART + RLENGTH - 1), sources
+ }
+ f = 1
+ while (/\\$/) { getline }
+ } else {
+ print
+ while (/\\$/) { getline; print }
+ }
+ next
+}
+
+{ print }
+
+END {
+ if (!f && sources) {
+ # append new entry
+ printf "%s%s" ORS, fmt_db(db_name":"), sources
+ }
+}
+' /etc/nsswitch.conf >/etc/nsswitch.conf.tmp \
+&& cat /etc/nsswitch.conf.tmp >/etc/nsswitch.conf
+rm -f /etc/nsswitch.conf.tmp
+EOF
diff --git a/type/__nsswitch/man.rst b/type/__nsswitch/man.rst
new file mode 100644
index 000000000..3aa0b6198
--- /dev/null
+++ b/type/__nsswitch/man.rst
@@ -0,0 +1,73 @@
+cdist-type__nsswitch(7)
+=======================
+
+NAME
+----
+cdist-type__nsswitch - manage Name Service Switch database configuration
+
+
+DESCRIPTION
+-----------
+This type can be used to manage database configurations in the
+:strong:`nsswitch.conf`\ (5) file.
+
+This type does no checking whether your operating system/libc respects the
+nsswitch.conf file, this job is up to you.
+
+
+OPTIONAL PARAMETERS
+-------------------
+source
+ Specify the name of a source for this database (with optional criteria
+ appended to the source name in square brackets ``[]``).
+ See :strong:`nsswitch.conf`\ (5) for the options supported by your operating
+ system.
+
+ Can be used multiple times so specify multiple sources which will be queried
+ in the order specified.
+
+ If no sources are given, the database entry will be removed from
+ :strong:`nsswitch.conf`\ (5).
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Configure NSS to query LDAP for users/groups (Linux)
+ __nsswitch passwd \
+ --source ldap \
+ --source files
+ __nsswitch group \
+ --source ldap \
+ --source files
+ __nsswitch shadow \
+ --source ldap \
+ --source files
+
+ # Configure NSS host lookup to include mDNS (using libnss-mdns on Linux)
+ # Other OSs may use different source names, e.g. mdns_minimal is called
+ # multicast_dns on NetBSD
+ __nsswitch hosts \
+ --source files \
+ --source 'mdns_minimal [NOTFOUND=return]' \
+ --source dns
+
+
+SEE ALSO
+--------
+* :strong:`nsswitch.conf`\ (5)
+
+
+AUTHORS
+-------
+* Dennis Camera
+
+
+COPYING
+-------
+Copyright \(C) 2023, 2025 Dennis Camera.
+You can redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
diff --git a/type/__nsswitch/nonparallel b/type/__nsswitch/nonparallel
new file mode 100644
index 000000000..e69de29bb
diff --git a/type/__nsswitch/parameter/optional_multiple b/type/__nsswitch/parameter/optional_multiple
new file mode 100644
index 000000000..5a18cd2fb
--- /dev/null
+++ b/type/__nsswitch/parameter/optional_multiple
@@ -0,0 +1 @@
+source