Docker Secrets
Deploy OpenLDAP with passwords loaded from Docker secrets instead of plaintext environment variables. Passwords never appear indocker inspect, process lists, or container metadata.
Project Files
openldap-secrets
Explorer
docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# Single-node OpenLDAP. # # Usage: # cp .env.example .env # optional - the defaults below work with no .env # docker compose up -d # # Compose reads .env automatically for the ${VAR:-default} interpolation here, # so no env_file is required and the stack starts on a fresh clone. services: openldap: image: ${LDAP_IMAGE:-vibhuvioio/openldap:latest} container_name: openldap restart: unless-stopped ports: - "389:389" - "636:636" environment: - LDAP_DOMAIN=${LDAP_DOMAIN:-example.com} - LDAP_ORGANIZATION=${LDAP_ORGANIZATION:-Example Organization} - LDAP_ADMIN_PASSWORD=${LDAP_ADMIN_PASSWORD:-changeme} - LDAP_CONFIG_PASSWORD=${LDAP_CONFIG_PASSWORD:-changeme} - ENABLE_REPLICATION=${ENABLE_REPLICATION:-false} - ENABLE_MONITORING=${ENABLE_MONITORING:-true} - ENABLE_MEMBEROF=${ENABLE_MEMBEROF:-false} - ENABLE_PASSWORD_POLICY=${ENABLE_PASSWORD_POLICY:-false} - ENABLE_AUDIT_LOG=${ENABLE_AUDIT_LOG:-false} - INCLUDE_SCHEMAS=${INCLUDE_SCHEMAS:-} - LDAP_LOG_LEVEL=${LDAP_LOG_LEVEL:-} volumes: - ldap-data:/var/lib/ldap - ldap-config:/etc/openldap/slapd.d - ldap-logs:/logs # Docker sends SIGTERM and then SIGKILL once stop_grace_period elapses. The # entrypoint waits up to 30s for slapd to drain, so the default 10s would # kill slapd mid-transaction and force a full database scan on next start. stop_grace_period: 30s # slapd uses one file descriptor per connection. ulimits: nofile: soft: 10240 hard: 10240 security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - DAC_READ_SEARCH - DAC_OVERRIDE - NET_BIND_SERVICE - SETUID - SETGID - CHOWN healthcheck: test: ["CMD", "/usr/local/bin/scripts/healthcheck.sh", "auto"] interval: 30s timeout: 10s retries: 3 # A cold start configures the database and waits for port 389 to be # released; a 30s start period marked a healthy container unhealthy while # it was still initialising. start_period: 90s deploy: resources: limits: memory: 512M cpus: "1.0" reservations: memory: 128M logging: driver: json-file options: max-size: "10m" max-file: "3" networks: - ldap-network volumes: ldap-data: driver: local ldap-config: driver: local ldap-logs: driver: local networks: ldap-network: driver: bridge
YAMLUTF-8
Ln 951 files
Set image
export LDAP_IMAGE=vibhuvioio/openldap:latest
Create Secret Files
mkdir -p secrets
echo "YourSecureAdminPassword" > secrets/admin_password.txt
echo "YourSecureConfigPassword" > secrets/config_password.txt
Important: Addsecrets/.txtto your.gitignoreto avoid committing passwords.
Start
docker compose up -d
Verify Secrets Are Used
# Passwords should NOT appear in environment variables
docker exec openldap-secrets env | grep -i password
# Expected: LDAP_ADMIN_PASSWORD_FILE=/run/secrets/ldap_admin_password
# (not the actual password)
# Verify the secret file is mounted
docker exec openldap-secrets cat /run/secrets/ldap_admin_password
Test Authentication
ADMIN_PASS=$(cat secrets/admin_password.txt)
ldapsearch -x -H ldap://localhost:389 \
-D "cn=Manager,dc=example,dc=com" \
-w "$ADMIN_PASS" \
-b "dc=example,dc=com" -s base
How It Works
The Docker Compose file uses the secrets directive:
services:
openldap:
environment:
- LDAP_ADMIN_PASSWORD_FILE=/run/secrets/ldap_admin_password
- LDAP_CONFIG_PASSWORD_FILE=/run/secrets/ldap_config_password
secrets:
- ldap_admin_password
- ldap_config_password
secrets:
ldap_admin_password:
file: ./secrets/admin_password.txt
ldap_config_password:
file: ./secrets/config_password.txt
The startup.sh script detects _FILE variants and reads the password from the file at runtime:
if [ -n "$LDAP_ADMIN_PASSWORD_FILE" ] && [ -f "$LDAP_ADMIN_PASSWORD_FILE" ]; then
LDAP_ADMIN_PASSWORD=$(cat "$LDAP_ADMIN_PASSWORD_FILE")
fi
Security Comparison
| Plaintext Env Var | Docker Secrets |
|---|---|
Visible in docker inspect | Hidden from container metadata |
In process list (ps e) | Only in file readable by container |
| May be logged | Not logged by default |
| Risk of git commit | Can be gitignored |
Connection Details
| Setting | Value |
|---|---|
| Host | localhost |
| LDAP Port | 389 |
| LDAPS Port | 636 |
| Bind DN | cn=Manager,dc=example,dc=com |
| Base DN | dc=example,dc=com |
| Password | Contents of secrets/admin_password.txt |
Production Recommendations
-
1
Never commit secrets to git — add
secrets/.txtto.gitignore -
2
Use proper secret managers — Docker Swarm (
docker secret create), Kubernetes Sealed Secrets, or HashiCorp Vault - 3 Rotate secrets regularly — update the file and restart the container
-
4
Set strict permissions —
chmod 600 secrets/*.txt
Cleanup
docker compose down -v