diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b26d23684e3e0d59528ead5c823ff595c8aeb440
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,48 @@
+~*
+*~
+*.sw[op]
+*.py[cod]
+.DS_Store
+__pycache__/
+.vagrant/
+vendor/
+Thumbs.db
+*.retry
+.svn/
+.sass-cache/
+*.log
+*.out
+*.so
+node_modules/
+.npm/
+nbproject/
+*.ipynb
+.idea/
+*.egg-info/
+*.[ao]
+.classpath
+.cache/
+bower_components/
+*.class
+*.[ewj]ar
+secring.*
+.*.kate-swp
+.swp.*
+.directory
+.Trash-*
+build/
+_build/
+dist/
+.tox/
+*.pdf
+*.exe
+*.dll
+*.gz
+*.tgz
+*.tar
+*.rar
+*.zip
+*.pid
+*.lock
+*.env
+.bundle/
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8f9fac4592132f4a7cdc5482bfc7baef1ba9dd12
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Adar Nimrod
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e850d03ec96ffe05c2677ac3ff8c54f204bb18e2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# LDAP Docker
+
+> A dockerized OpenLDAP with phpLDAPadmin webui.
+
+## Requirements
+
+- Docker
+- Docker Compose
+
+## License
+
+This software is licensed under the MIT license (see `LICENSE.txt`).
+
+## Author Information
+
+Nimrod Adar, [contact me](mailto:nimrod@shore.co.il) or visit my [website](
+https://www.shore.co.il/). Patches are welcome via [`git send-email`](
+http://git-scm.com/book/en/v2/Git-Commands-Email). The repository is located
+at: <https://www.shore.co.il/git/>.
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b6a3644e65ba0093afdcada94357a4508ea68a77
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,29 @@
+---
+version: '3'
+services:
+    slapd:
+        build:
+            context: slapd/
+        volumes:
+            - _run_ldap:/run/slapd
+            - ldap:/var/lib/ldap
+        environment:
+            LDAP_ROOTPASS: foo
+            LDAP_DOMAIN: nowhere.com
+            LDAP_ORGANIZATION: none
+    phpldapadmin:
+        build:
+            context: phpldapadmin/
+        links:
+            - slapd
+        volumes:
+            - _run_ldap:/run/slapd
+        environment:
+            PLA_HOST: ldapi://%2frun%2fslapd%2fldapi
+            PLA_BASE_DN: 'dc=nowhere,dc=com'
+            PLA_BIND_ID: 'cn=admin,dc=nowhere,dc=com'
+        ports:
+            - 80:80
+volumes:
+    _run_ldap:
+    ldap:
diff --git a/phpldapadmin/.dockerignore b/phpldapadmin/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..dd449725e188f816bcebfc05678064efcbc29a81
--- /dev/null
+++ b/phpldapadmin/.dockerignore
@@ -0,0 +1 @@
+*.md
diff --git a/phpldapadmin/Dockerfile b/phpldapadmin/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..05209213199aaaf91360eb51f5809aab81cf4585
--- /dev/null
+++ b/phpldapadmin/Dockerfile
@@ -0,0 +1,10 @@
+FROM alpine:3.8
+RUN apk add --update --no-cache phpldapadmin php5-apache2 && \
+    ln -sf /dev/stdout /var/log/apache2/access.log && \
+    ln -sf /dev/stderr /var/log/apache2/error.log && \
+    mkdir -p /run/apache2/
+
+COPY --chown=root:root config.php /usr/share/webapps/phpldapadmin/config/
+COPY --chown=root:root phpldapadmin.conf /etc/apache2/conf.d/
+CMD [ "httpd", "-DFOREGROUND" ]
+HEALTHCHECK CMD wget --spider --quiet http://localhost/htdocs/index.php || exit 1
diff --git a/phpldapadmin/README.md b/phpldapadmin/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..296b2c6ddea0e372dff3976a3d5dffc350f42bf2
--- /dev/null
+++ b/phpldapadmin/README.md
@@ -0,0 +1,15 @@
+# phpLDAPadmin
+
+> Dockerized phpLDAPadmin.
+
+## Environment variables
+
+Name | Default value
+--- | ---
+`PLA_NAME` | `LDAP server`
+`PLA_HOST` | `slapd`
+`PLA_PORT` | `389`
+`PLA_BASE` |
+`PLA_AUTH_TYPE` | `cookie`
+`PLA_BIND_ID` |
+`PLA_TLS` | `false`
diff --git a/phpldapadmin/config.php b/phpldapadmin/config.php
new file mode 100644
index 0000000000000000000000000000000000000000..616618e2787f09e89d3f21799f0399d7968549b3
--- /dev/null
+++ b/phpldapadmin/config.php
@@ -0,0 +1,14 @@
+<?php
+
+$servers = new Datastore();
+
+$servers->newServer('ldap_pla');
+$servers->setValue('server', 'name', getenv('PLA_NAME') ?: 'LDAP Server');
+$servers->setValue('server', 'host', getenv('PLA_HOST') ?: 'slapd');
+$servers->setValue('server', 'port', getenv('PLA_PORT') ?: '389');
+$servers->setValue('server', 'base', array(getenv('PLA_BASE_DN') ?: ''));
+$servers->setValue('login', 'auth_type', getenv('PLA_AUTH_TYPE') ?: 'cookie');
+$servers->setValue('login', 'bind_id', getenv('PLA_BIND_ID') ?: '');
+$servers->setValue('server', 'tls', strtolower(getenv('PLA_TLS') ?: 'false') == 'true');
+
+?>
diff --git a/phpldapadmin/phpldapadmin.conf b/phpldapadmin/phpldapadmin.conf
new file mode 100644
index 0000000000000000000000000000000000000000..8d78331e1ce2678d8d3855d31c23facaaaff1e34
--- /dev/null
+++ b/phpldapadmin/phpldapadmin.conf
@@ -0,0 +1,39 @@
+<VirtualHost _default_:80>
+    DocumentRoot /usr/share/webapps/phpldapadmin/
+</VirtualHost>
+
+<Directory /usr/share/webapps/phpldapadmin/>
+
+    DirectoryIndex index.php
+    Options +FollowSymLinks
+    AllowOverride None
+
+    Require all granted
+
+    <IfModule mod_mime.c>
+
+      <IfModule mod_php5.c>
+        AddType application/x-httpd-php .php
+
+        php_flag magic_quotes_gpc Off
+        php_flag track_vars On
+        php_flag register_globals Off
+        php_value include_path .
+      </IfModule>
+
+      <IfModule !mod_php5.c>
+        <IfModule mod_actions.c>
+          <IfModule mod_cgi.c>
+            AddType application/x-httpd-php .php
+            Action application/x-httpd-php /cgi-bin/php5
+          </IfModule>
+          <IfModule mod_cgid.c>
+            AddType application/x-httpd-php .php
+            Action application/x-httpd-php /cgi-bin/php5
+           </IfModule>
+        </IfModule>
+      </IfModule>
+
+    </IfModule>
+
+</Directory>
diff --git a/slapd/.dockerignore b/slapd/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..dd449725e188f816bcebfc05678064efcbc29a81
--- /dev/null
+++ b/slapd/.dockerignore
@@ -0,0 +1 @@
+*.md
diff --git a/slapd/Dockerfile b/slapd/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..8b173d6382880a380331dfcd176afa99b5e61f5f
--- /dev/null
+++ b/slapd/Dockerfile
@@ -0,0 +1,16 @@
+FROM debian:stretch-slim
+RUN apt-get update && \
+    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+        gnutls-bin \
+        ldap-utils \
+        slapd \
+    && \
+    mkdir -p /run/slapd && \
+    rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/* /var/cache/apt/archives/*
+COPY entrypoint /
+EXPOSE 389
+VOLUME [ "/var/lib/ldap" ]
+ENV LDAP_URLS="ldap:/// ldapi:/// ldaps:///"
+ENTRYPOINT [ "/entrypoint" ]
+CMD [ "slapd", "-F", "/etc/ldap/slapd.d", "-u", "openldap", "-g", "openldap", "-h", "\"$LDAP_URLS\"", "-d", "NONE" ]
+HEALTHCHECK CMD ldapsearch -b cn=config -H ldapi:/// > /dev/null || exit 1
diff --git a/slapd/README.md b/slapd/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..326c49fbb41bda911890739a8c0c44dd039103f7
--- /dev/null
+++ b/slapd/README.md
@@ -0,0 +1,16 @@
+# slapd
+
+> Dockerized OpenLDAP daemon.
+
+## Environment variables
+
+Name | Description | Default value
+--- | --- | ---
+`LDAP_URLS` | List of URLs to serve. | `ldap:/// ldapi:/// ldaps:///`
+`LDAP_ROOTPASS` | Root password.
+`LDAP_DOMAIN` | Domain.
+`LDAP_ORGANIZATION` | Organization.
+
+## Persistence
+
+The database is at `/var/lib/ldap`.
diff --git a/slapd/entrypoint b/slapd/entrypoint
new file mode 100755
index 0000000000000000000000000000000000000000..00205a9db9cdeef9c5e5ce7b57b1c6f1d1c7eea7
--- /dev/null
+++ b/slapd/entrypoint
@@ -0,0 +1,36 @@
+#!/bin/sh
+set -eux
+
+chown -R openldap:openldap /run/slapd
+chown -R openldap:openldap /var/lib/ldap
+
+if [ -n "${LDAP_ROOTPASS:-}" ]
+then
+cat <<EOF | debconf-set-selections
+slapd slapd/internal/generated_adminpw password ${LDAP_ROOTPASS}
+slapd slapd/internal/adminpw password ${LDAP_ROOTPASS}
+slapd slapd/password2 password ${LDAP_ROOTPASS}
+slapd slapd/password1 password ${LDAP_ROOTPASS}
+EOF
+fi
+
+if [ -n "${LDAP_DOMAIN:-}" ]
+then
+cat <<EOF | debconf-set-selections
+slapd slapd/domain string ${LDAP_DOMAIN}
+EOF
+fi
+
+if [ -n "${LDAP_ORGANIZATION:-}" ]
+then
+cat <<EOF | debconf-set-selections
+slapd shared/organization string ${LDAP_ORGANIZATION}
+EOF
+fi
+
+if [ -n "${LDAP_ROOTPASS:-}" ] || [ -n "${LDAP_DOMAIN:-}" ] || [ -n "${LDAP_ORGANIZATION:-}" ]
+then
+    dpkg-reconfigure -f noninteractive slapd
+fi
+
+eval exec "$@"