Why I Set This Up
I run multiple container environments across different teams in my homelab—development, staging, and production workloads spread across Proxmox nodes. Managing who can access what became messy fast. I needed proper role-based access control (RBAC) that didn't require me to manually create users every time someone joined a project or left a team.
I already had an LDAP directory running for other services, so integrating Portainer with it made sense. The goal was simple: let people authenticate with their existing credentials, automatically assign them to the right teams based on LDAP group membership, and control what environments they could touch.
My Setup
I'm running Portainer Business Edition (the free tier supports up to 5 nodes) on a dedicated LXC container in Proxmox. My LDAP server is OpenLDAP, also running in a container. I manage about 8 Docker hosts—some physical, some VMs—and wanted centralized container management without giving everyone admin access.
Here's what I needed to solve:
- Automatic user provisioning when someone logs in via LDAP
- Team assignment based on LDAP group membership
- Environment-level access control (dev team sees dev stacks, prod team sees prod)
- No manual user creation unless absolutely necessary
Configuring LDAP Authentication
I logged into Portainer as admin, went to Settings → Authentication, and selected LDAP Authentication. The interface is straightforward, but there are some specific settings that took trial and error to get right.
Automatic User Provisioning
I enabled Automatic user provisioning immediately. Without this, I'd have to manually create a Portainer user for every LDAP user before they could log in. That defeats the entire purpose. With it enabled, when someone authenticates successfully via LDAP, Portainer creates their account automatically.
LDAP Server Configuration
For my OpenLDAP setup, I used these settings:
- Server Address:
ldap://192.168.1.50:389(my LDAP container's IP) - Reader DN:
cn=portainer-reader,dc=homelab,dc=local - Reader Password: A dedicated read-only service account I created in LDAP
I clicked Test connectivity and got a success response. If this fails, nothing else will work, so this step is critical.
Security Settings
I'm not using TLS for LDAP in my internal network. It's isolated behind VLANs, and adding certificate management for internal LDAP felt like overkill. If I were exposing this externally or dealing with sensitive data, I'd absolutely enable Use StartTLS or Use TLS and upload the CA certificate.
For now, my setup is:
- Use StartTLS: Disabled
- Use TLS: Disabled
- Skip verification: Not applicable
User Search Configuration
This is where things got specific to my directory structure. My users live under ou=users,dc=homelab,dc=local, so I set:
- Base DN:
ou=users,dc=homelab,dc=local - Username Attribute:
uid(standard for OpenLDAP) - Filter:
(&(objectClass=inetOrgPerson)(memberOf=cn=portainer-users,ou=groups,dc=homelab,dc=local))
The filter is important. I only want users who are members of the portainer-users LDAP group to be able to log in. Without this filter, anyone in my LDAP directory could authenticate, which I didn't want.
Group Search Configuration
This is where the real power of LDAP integration shows up. I wanted LDAP group membership to automatically map to Portainer teams. For example, if someone is in the dev-team LDAP group, they should automatically be added to the dev-team Portainer team.
My settings:
- Group Base DN:
ou=groups,dc=homelab,dc=local - Group Membership Attribute:
member - Group Filter:
(&(objectClass=groupOfNames)(cn=*-team))
This filter matches any LDAP group ending in -team. I have groups like dev-team, ops-team, and prod-team, and this filter catches all of them.
Once configured, I clicked Fetch Admin Group(s) to verify it was pulling the correct groups from LDAP. It worked, showing all my team groups.
Creating Portainer Teams
LDAP group membership alone doesn't grant access to environments. I had to create matching teams in Portainer manually. I went to Users → Teams and created teams with the exact same names as my LDAP groups:
dev-teamops-teamprod-team
The names must match exactly—case-sensitive. If your LDAP group is Dev-Team and your Portainer team is dev-team, the automatic assignment won't work.
Assigning Environment Access
After creating teams, I went to Environments and configured access for each Docker host. For example:
- dev-docker: Only
dev-teamhas access - prod-docker: Only
prod-teamandops-teamhave access
This is done under Environment → Access control. I selected the team, assigned either Standard or Restricted access, and saved. Standard access lets them deploy stacks and manage containers. Restricted access is read-only.
Testing Authentication
Portainer has a built-in Test login feature under the LDAP settings. I entered a test user's credentials and clicked Test. It returned:
- Authentication successful
- User groups retrieved:
dev-team - User would be added to Portainer team:
dev-team
This confirmed the entire flow was working before I had actual users try to log in.
What Worked
Once configured, the system worked exactly as intended. When a user logs in for the first time:
- Portainer authenticates them against LDAP
- If authentication succeeds, a Portainer user account is created automatically
- Portainer checks their LDAP group memberships
- They're added to matching Portainer teams
- They see only the environments their team has access to
When I add someone to an LDAP group, they automatically get access to the corresponding Portainer team on their next login. No manual intervention required.
What Didn't Work
Case Sensitivity Confusion
My first attempt failed because my LDAP group was DevTeam and my Portainer team was dev-team. The automatic team assignment silently failed. Portainer doesn't warn you about this—it just doesn't add the user to the team. I had to match the names exactly.
Filter Syntax Errors
LDAP filter syntax is unforgiving. I initially wrote:
(&(objectClass=groupOfNames)(cn=*team))
This didn't match groups ending in -team. I had to change it to:
(&(objectClass=groupOfNames)(cn=*-team))
The difference is subtle, but LDAP filters are literal. There's no fuzzy matching.
Nested Group Membership
Portainer doesn't support nested LDAP groups. If you have a structure like:
all-users(parent group)dev-team(member ofall-users)
A user in dev-team won't inherit membership in all-users from Portainer's perspective. You have to add users directly to the groups you want Portainer to recognize.
Key Takeaways
- LDAP integration with Portainer works well once configured correctly, but the initial setup requires precise matching of group names and careful filter syntax.
- Automatic user provisioning eliminates manual account creation, but you still need to manually create Portainer teams that match your LDAP groups.
- Environment-level access control is powerful but requires upfront planning. Decide which teams need access to which environments before you start.
- The test login feature is invaluable. Use it to validate your configuration before rolling it out to users.
- LDAP group changes propagate on the next login, not immediately. If you add someone to a group, they need to log out and back in to see the new environments.
This setup has been running for several months now. I haven't had to manually create a Portainer user since I enabled LDAP. Team access is managed entirely through LDAP group membership, which makes onboarding and offboarding straightforward. When someone leaves a project, I remove them from the LDAP group, and they lose access to the corresponding Portainer environments on their next login.