Initialization Scripts

Run custom scripts on the first container startup to automate setup tasks like loading data, creating organizational units, or applying custom configurations.

How It Works

When the container starts for the first time (empty database), it executes any files in /docker-entrypoint-initdb.d/:
  • Shell scripts (.sh) — executed with bash
  • LDIF files (.ldif) — applied via ldapadd/ldapmodify
  • Executable files — run directly
Important: Scripts only run on first startup. On restart, the container detects existing data and skips initialization.

Quick Start

Create an init script to load sample data:

mkdir -p init
cat > init/01-load-data.sh << 'EOF'
#!/bin/bash
set -e

echo "Loading sample data..."

ldapadd -x -D "cn=Manager,dc=example,dc=com" -w "$LDAP_ADMIN_PASSWORD" <<LDIF
dn: ou=Departments,dc=example,dc=com
objectClass: organizationalUnit
ou: Departments

dn: ou=Engineering,ou=Departments,dc=example,dc=com
objectClass: organizationalUnit
ou: Engineering

dn: uid=john.doe,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
uid: john.doe
cn: John Doe
sn: Doe
givenName: John
mail: john.doe@example.com
userPassword: {SSHA}encryptedpassword
LDIF

echo "Sample data loaded successfully!"
EOF

chmod +x init/01-load-data.sh

Mount and run:

services:
  openldap:
    image: ghcr.io/vibhuvioio/openldap:latest
    environment:
      - LDAP_DOMAIN=example.com
      - LDAP_ADMIN_PASSWORD=changeme
    volumes:
      - ./init:/docker-entrypoint-initdb.d:ro
docker compose up -d
# Watch the initialization
docker logs -f openldap

Common Use Cases

1. Load Sample Data

Create init/01-data.sh:

#!/bin/bash
set -e

echo "Creating organizational structure..."

ldapadd -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" <<LDIF
dn: ou=Departments,dc=example,dc=com
objectClass: organizationalUnit
ou: Departments

dn: ou=Engineering,ou=Departments,dc=example,dc=com
objectClass: organizationalUnit
ou: Engineering

dn: ou=Sales,ou=Departments,dc=example,dc=com
objectClass: organizationalUnit
ou: Sales
LDIF

echo "Creating test users..."

for i in {1..5}; do
  ldapadd -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" <<LDIF
dn: uid=user$i,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
uid: user$i
cn: Test User $i
sn: User
mail: user$i@example.com
userPassword: password$i
LDIF
done

2. Apply Custom Schema

Create init/02-schema.ldif:

dn: cn=myapp,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: myapp
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1
  NAME 'appRole'
  DESC 'Application role'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{64} )
olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1
  NAME 'appUser'
  DESC 'Application user'
  SUP inetOrgPerson
  STRUCTURAL
  MAY ( appRole ) )

Apply with a wrapper script init/02-schema.sh:

#!/bin/bash
set -e

echo "Loading custom schema..."
ldapadd -Y EXTERNAL -H ldapi:/// -f /docker-entrypoint-initdb.d/02-schema.ldif

echo "Creating app users..."
ldapadd -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" <<LDIF
dn: ou=AppUsers,dc=example,dc=com
objectClass: organizationalUnit
ou: AppUsers

dn: uid=appadmin,ou=AppUsers,dc=example,dc=com
objectClass: appUser
uid: appadmin
cn: App Admin
sn: Admin
appRole: administrator
userPassword: adminpass
LDIF

3. Increase Database Size

Create init/00-database-config.sh:

#!/bin/bash
set -e

echo "Configuring database size..."

ldapmodify -Y EXTERNAL -H ldapi:/// <<LDIF
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcDbMaxSize
olcDbMaxSize: 2147483648
LDIF

echo "Database size set to 2GB"
Run this as 00-*.sh to ensure it executes before data loading.

4. Create Groups with Members

Create init/03-groups.sh:

#!/bin/bash
set -e

echo "Creating groups..."

ldapadd -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" <<LDIF
dn: cn=admins,ou=Group,dc=example,dc=com
objectClass: groupOfNames
cn: admins
member: uid=john.doe,ou=People,dc=example,dc=com

dn: cn=developers,ou=Group,dc=example,dc=com
objectClass: groupOfNames
cn: developers
member: uid=jane.doe,ou=People,dc=example,dc=com
member: uid=bob.smith,ou=People,dc=example,dc=com
LDIF

Execution Order

Scripts execute in alphabetical order. Use numeric prefixes to control order:

init/
├── 00-database-config.sh    # First: configure database
├── 01-schema.sh             # Second: load custom schema
├── 02-ous.sh                # Third: create OUs
└── 03-users.sh              # Last: create users

Environment Variables Available

Scripts have access to these environment variables:

VariableDescriptionExample
LDAP_ADMIN_DNFull admin DNcn=Manager,dc=example,dc=com
LDAP_ADMIN_PASSWORDAdmin passwordchangeme
LDAP_BASE_DNBase DNdc=example,dc=com
LDAP_DOMAINDomainexample.com

Error Handling

Scripts should use set -e to fail on errors:

#!/bin/bash
set -e  # Exit immediately if a command fails

# Your commands here

If a script fails, the container logs the error but continues starting. Check logs:

docker logs openldap | grep -i error

Idempotency

Scripts may run if the database is wiped. Make them idempotent when possible:

#!/bin/bash
set -e

# Check if entry exists before creating
if ! ldapsearch -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" \
     -b "ou=Departments,dc=example,dc=com" -s base 2>/dev/null | grep -q "ou:"; then
    echo "Creating Departments OU..."
    ldapadd -x -D "$LDAP_ADMIN_DN" -w "$LDAP_ADMIN_PASSWORD" <<LDIF
dn: ou=Departments,dc=example,dc=com
objectClass: organizationalUnit
ou: Departments
LDIF
else
    echo "Departments OU already exists, skipping..."
fi

Download Example

openldap-init
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
96
97
services:
  openldap:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: openldap
    environment:
      # Required
      - LDAP_DOMAIN=example.com
      - LDAP_ADMIN_PASSWORD=changeme-strong-password
      
      # Optional (defaults shown)
      # - LDAP_ORGANIZATION=Example Organization
      # - LDAP_CONFIG_PASSWORD=config
      # - ENABLE_REPLICATION=false
      # - ENABLE_MONITORING=true
      # - ENABLE_MEMBEROF=false
      # - LDAP_LOG_LEVEL=stats stats2
    ports:
      - "389:389"
      - "636:636"
    volumes:
      # Required: Persistent data
      - ldap-data:/var/lib/ldap
      - ldap-config:/etc/openldap/slapd.d
      
      # Required: Logs (even with read-only rootfs)
      - ldap-logs:/logs
      
      # Optional: Custom schemas
      # - ./custom-schema:/custom-schema:ro
      
      # Optional: Initialization scripts
      # - ./init-scripts:/docker-entrypoint-initdb.d:ro
      
      # Optional: TLS certificates
      # - ./certs:/certs:ro
    
    # Security: Read-only rootfs with tmpfs for temporary files
    read_only: true
    tmpfs:
      - /tmp
      - /var/run/openldap
    
    # Security: Drop all capabilities and add only required ones
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
      - SETUID
      - SETGID
      - CHOWN
    
    # Grace period for clean shutdown
    stop_grace_period: 30s
    
    # Health check built into image
    healthcheck:
      test: ["CMD", "/usr/local/bin/scripts/healthcheck.sh", "basic"]
      interval: 30s
      timeout: 5s
      start_period: 30s
      retries: 3
    
    restart: unless-stopped
    
    networks:
      - ldap-network
    
    # Resource limits for production stability
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
        reservations:
          memory: 128M
    
    # Log rotation to prevent disk exhaustion
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  ldap-data:
  ldap-config:
  ldap-logs:

# Custom network for service isolation
networks:
  ldap-network:
    driver: bridge
YAMLUTF-8
Ln 972 files

Connection Details

SettingValue
Hostlocalhost
LDAP Port389
Bind DNcn=Manager,dc=example,dc=com (or your custom)
PasswordAs configured in LDAP_ADMIN_PASSWORD

Cleanup

# Stop and remove data (runs init scripts again on next start)
docker compose down -v

# Stop but keep data (skips init scripts on next start)
docker compose down

Troubleshooting

Scripts not running:
  • Check container logs: docker logs openldap | grep -i init
  • Verify scripts are executable: chmod +x init/.sh
  • Ensure database is empty (scripts only run on first startup)
"Entry already exists" errors:
  • Container was restarted with existing data (expected behavior)
  • Or script isn't idempotent and ran twice due to container recreation
Permission denied:
  • Ensure scripts have execute permission: chmod +x init/.sh
  • Check file ownership matches container user (UID 55)