Dockhand User Manual
Welcome to Dockhand, a modern, powerful Docker management application. This manual covers all features and functionality to help you get the most out of Dockhand.
In the free edition, all authenticated users have full admin access to all environments and features. There are no role restrictions - if you can log in, you can do everything.
The Enterprise edition adds Role-Based Access Control (RBAC), allowing you to define custom roles with specific permissions and restrict access to individual environments. See the Enterprise features section for details.
Quick start
Get Dockhand running in minutes with Docker:
docker run -d \
--name dockhand \
--restart unless-stopped \
-p 3000:3000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v dockhand_data:/app/data \
fnsys/dockhand:v1.0.0
Or using Docker Compose:
services:
dockhand:
image: fnsys/dockhand:v1.0.0
container_name: dockhand
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockhand_data:/app/data
volumes:
dockhand_data:
Then open http://localhost:3000 in your browser.
On first launch, authentication is disabled. Go to Settings > Authentication to enable authentication and create your first admin user.
Docker socket permissions
Dockhand needs access to Docker to manage containers. By default, the Dockhand container runs as a non-root user, which may not have permission to access the socket on your host system.
If you see a "permission denied" error when trying to add your local environment, you'll need to configure socket access. Here are the options:
Match the Docker group GID
Find your host's Docker group ID and run Dockhand with that group:
# Find the Docker group ID on your host (Linux)
stat -c '%g' /var/run/docker.sock
# On macOS, use: stat -f '%g' /var/run/docker.sock
# Example output: 999
# Run Dockhand with the matching group
docker run -d \
--name dockhand \
--restart unless-stopped \
--group-add 999 \
-p 3000:3000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v dockhand_data:/app/data \
fnsys/dockhand:v1.0.0
Or using Docker Compose:
services:
dockhand:
image: fnsys/dockhand:v1.0.0
container_name: dockhand
restart: unless-stopped
group_add:
- "999" # Replace with your host's Docker GID
ports:
- "3000:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockhand_data:/app/data
volumes:
dockhand_data:
Run as root user
SimplestRunning as root (0:0) bypasses all permission checks. This is the simplest solution but gives the container full root privileges:
docker run -d \
--name dockhand \
--restart unless-stopped \
--user 0:0 \
-p 3000:3000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v dockhand_data:/app/data \
fnsys/dockhand:v1.0.0
Or using Docker Compose:
services:
dockhand:
image: fnsys/dockhand:v1.0.0
container_name: dockhand
restart: unless-stopped
user: "0:0"
ports:
- "3000:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockhand_data:/app/data
volumes:
dockhand_data:
Running as root is convenient but less secure. Anyone with access to Dockhand can potentially access the Docker daemon with full privileges. Use Option 4 in production environments where security is a concern.
Change socket permissions on host
Not recommendedYou can make the Docker socket world-readable, but this exposes it to all users on the host:
# NOT RECOMMENDED - makes socket accessible to everyone
sudo chmod 666 /var/run/docker.sock
This change affects all processes on the host, not just Dockhand. Any user or container can now access Docker. The permission resets on Docker daemon restart.
Docker socket proxy
Most secureIf you want truly secure access, place a proxy between Dockhand and the actual Docker socket. This filters which API calls are allowed. A popular tool for this is tecnativa/docker-socket-proxy.
services:
socket-proxy:
image: tecnativa/docker-socket-proxy
container_name: socket-proxy
restart: unless-stopped
environment:
# Required for Dockhand core functionality
- CONTAINERS=1
- IMAGES=1
- NETWORKS=1
- VOLUMES=1
- EVENTS=1
- POST=1
- DELETE=1
# Optional: enable for terminal access
# - EXEC=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket-proxy
dockhand:
image: fnsys/dockhand:v1.0.0
container_name: dockhand
restart: unless-stopped
depends_on:
- socket-proxy
ports:
- "3000:3000"
volumes:
- dockhand_data:/app/data
networks:
- socket-proxy
- default
networks:
socket-proxy:
internal: true
volumes:
dockhand_data:
See the full list of environment variables for additional options.
After starting the stack, configure Dockhand to use the proxy:
- Go to Settings > Environments
- Add a new environment or edit the default one
- Set connection type to Direct
- Set the host to
tcp://socket-proxy:2375 - Save the environment
The proxy acts as a firewall for the Docker API. It allows only necessary commands (list, start, stop containers) but can block dangerous ones (docker run --privileged, system commands). Dockhand connects to the proxy via a private Docker network, isolated from the raw socket.
The socket proxy container itself requires elevated privileges to access the Docker socket (typically --privileged or additional AppArmor/SELinux rules), which transfers some security risk to the proxy container.
More importantly: even with a socket proxy, anyone who gains access to Dockhand still has significant capabilities — starting/stopping containers, deleting volumes, pulling images, viewing logs, and more. The proxy limits the most dangerous operations but doesn't make Dockhand "safe to expose publicly".
Always restrict access to Dockhand itself using a reverse proxy with authentication, VPN (Tailscale/WireGuard), SSO/OIDC, or network segmentation. Treat Dockhand as an admin interface that should never be directly exposed to the internet.
Recommendations by environment
Home lab or private server behind a VPN (Tailscale/WireGuard):
Option 1 (GID matching) or Option 2 (root) is acceptable. The risk is manageable assuming your network is secure and you trust the Dockhand application code.
Public-facing server or production environment:
Consider using a Socket Proxy (Option 4) or restricting access via a VPN.
Security summary
| Method | Container User | Socket Access | Security Level | Risk |
|---|---|---|---|---|
| Option 1 (GID) | Non-root | Direct | Low/Medium | Root-equivalent if app is exploited |
| Option 2 (Root) | Root | Direct | None | Immediate root if app is exploited |
| Option 3 (chmod) | Any | Direct | None | All host users can access Docker |
| Option 4 (Proxy) | Non-root | Filtered | Higher* | Blocks dangerous API calls, but depends on enabled endpoints |
* Higher than direct socket access, but actual security depends on proxy configuration (which API endpoints are enabled) and container hardening.
Platform notes
Docker on Windows uses a named pipe instead of a Unix socket. In your volume mount, replace the socket path with:
-v //./pipe/docker_engine://./pipe/docker_engine
The permission options above (GID matching, chmod) do not apply to Windows.
Deployment examples
Behind a reverse proxy (Traefik)
When running behind Traefik or another reverse proxy, you don't need to expose ports directly:
services:
dockhand:
image: fnsys/dockhand:v1.0.0
container_name: dockhand
restart: unless-stopped
user: "0:0"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockhand_data:/app/data
networks:
- proxy # Must match your Traefik network
labels:
- "traefik.enable=true"
- "traefik.http.routers.dockhand.rule=Host(`dockhand.example.com`)"
- "traefik.http.routers.dockhand.entrypoints=websecure"
- "traefik.http.routers.dockhand.tls=true"
- "traefik.http.routers.dockhand.tls.certresolver=letsencrypt"
- "traefik.http.services.dockhand.loadbalancer.server.port=3000"
- "traefik.docker.network=proxy" # Required if container has multiple networks
networks:
proxy:
external: true # Assumes Traefik network already exists
volumes:
dockhand_data:
With Nginx reverse proxy
Example Nginx configuration for proxying to Dockhand:
server {
listen 443 ssl http2;
server_name dockhand.example.com;
ssl_certificate /etc/ssl/certs/dockhand.crt;
ssl_certificate_key /etc/ssl/private/dockhand.key;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Dockhand uses WebSockets for the terminal and real-time updates. Make sure your reverse proxy is configured to handle WebSocket connections (the Upgrade and Connection headers).
With Tailscale (VPN-only access)
The simplest and usually most secure approach: Traefik listens on 80/443, but ports are only accessible within your Tailnet (no public port exposure on the host).
services:
tailscale:
image: tailscale/tailscale:latest
hostname: homelab-gw
environment:
- TS_AUTHKEY=${TS_AUTHKEY}
- TS_STATE_DIR=/var/lib/tailscale
volumes:
- tailscale_state:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
cap_add:
- net_admin
- sys_module
restart: unless-stopped
socket-proxy:
image: tecnativa/docker-socket-proxy
environment:
- CONTAINERS=1
- SERVICES=1
- NETWORKS=1
- EVENTS=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks: [socket_proxy]
restart: unless-stopped
security_opt:
- no-new-privileges:true
traefik:
image: traefik:v3
network_mode: service:tailscale
depends_on: [socket-proxy]
command:
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.endpoint=tcp://socket-proxy:2375
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
volumes:
- traefik_data:/data
restart: unless-stopped
dockhand:
image: fnsys/dockhand:v1.0.0
labels:
- traefik.enable=true
- traefik.http.routers.dockhand.rule=Host(`dockhand.homelab-gw.ts.net`)
- traefik.http.routers.dockhand.entrypoints=websecure
- traefik.http.routers.dockhand.tls=true
- traefik.http.services.dockhand.loadbalancer.server.port=3000
networks: [default]
restart: unless-stopped
volumes:
- dockhand_data:/app/data
networks:
socket_proxy:
internal: true
volumes:
tailscale_state:
traefik_data:
dockhand_data:
This works because Traefik "inherits" the Tailscale network via network_mode: service:tailscale, so ports 80/443 are reachable at the Tailscale address (MagicDNS) but not exposed publicly.
Using PostgreSQL database
Dockhand can also use PostgreSQL instead of the default SQLite:
docker run -d \
--name dockhand \
--restart unless-stopped \
--user 0:0 \
-p 3000:3000 \
-e DATABASE_URL=postgres://user:password@postgres-host:5432/dockhand \
-v /var/run/docker.sock:/var/run/docker.sock \
-v dockhand_data:/app/data \
fnsys/dockhand:v1.0.0
The database schema is created automatically on first run. Just ensure the database exists and the user has sufficient privileges.
Switching from SQLite (default) to PostgreSQL does not migrate existing data. You will start with a fresh database.
Managing remote Docker hosts
Dockhand can manage Docker hosts beyond your local machine. There are several ways to connect to remote environments:
| Connection Type | Use Case | Requirements |
|---|---|---|
| Local socket | Same machine as Dockhand | Docker socket mounted |
| Direct TCP | Remote Docker with exposed API | Docker API on port 2375/2376 |
| Hawser Standard | LAN/homelab with static IPs | Hawser agent on remote host |
| Hawser Edge | VPS, NAT, dynamic IP, firewalled hosts | Hawser agent (outbound only) |
Hawser is a lightweight agent that enables secure remote Docker management without exposing the Docker API directly. It's especially useful for:
- Hosts behind NAT or firewalls (Edge mode - no inbound ports required)
- VPS or cloud instances with dynamic IPs
- Secure access without exposing Docker TCP port
- Homelab environments across different networks
See the Hawser section for detailed setup instructions.
System requirements
| Component | Requirement |
|---|---|
| Docker Engine | 20.10 or later |
| Docker API | 1.41 or later |
| Memory | 512 MB minimum, 1 GB recommended |
| Browser | Chrome, Firefox, Safari, Edge (latest versions) |
| Database | SQLite (default) or PostgreSQL 14+ |
Dashboard
The dashboard provides a real-time overview of all your Docker environments. Each environment is displayed as a tile that can be resized and repositioned to suit your workflow.
Environment tiles
Each environment tile displays key information at a glance:
- Header - Environment name, icon, and connection status
- Container counts - Running, stopped, and total containers
- Resource metrics - CPU and memory usage with progress bars
- Health status - Warning banner for unhealthy or restarting containers
- Activity summary - Today's events and recent activity
- Top containers - Containers sorted by CPU usage (larger tiles)
Status indicators
Tiles display status icons in the header:
| Icon | Status | Description |
|---|---|---|
Shield (green glow) |
Scanner enabled | Vulnerability scanning is active for this environment |
Activity (amber glow) |
Activity collection | Container events are being tracked |
WifiOff (red) |
Offline | Environment is unreachable |
Tile sizing
Tiles can be resized to show more or less information. Drag the corner of any tile to resize it.
| Size | Content |
|---|---|
| 1x1 (Compact) | Header, container counts, CPU/Memory bars |
| 2x1 (Wide compact) | Same as 1x1 with more horizontal space |
| 1x2 (Standard) | Container stats, health banner, resource metrics, resources summary |
| 1x3 (Detailed) | All 1x2 content + recent events list (8 items) |
| 1x4+ (Extra tall) | All 1x3 content + top containers by CPU |
| 2x2 (Square) | Two columns: stats on left, top containers on right |
| 2x3 (Wide tall) | Two columns: stats + events, containers + charts |
| 2x4+ (Full) | Two columns with CPU/Memory charts and disk usage breakdown |
Layout management
The dashboard provides several ways to organize your tiles:
Auto-layout presets
Use the dropdown in the header to quickly apply preset layouts:
- Compact - All tiles at 1x1 size
- Standard - All tiles at 1x2 size
- Detailed - All tiles at 1x3 size
- Full - All tiles at 2x3 size
Drag and drop
Drag tiles by their header to reposition them. The layout is automatically saved to your browser's local storage.
Label filtering
If you've assigned labels to environments, you can filter the dashboard to show only environments with specific labels. Click the label pills at the top of the dashboard to toggle filtering.
Dashboard data is streamed in real-time using Server-Sent Events (SSE). Metrics update every 10 seconds, and container events appear instantly.
Containers
The Containers page provides comprehensive management of all Docker containers across your environments.
List view
The container list displays all containers with sortable columns:
| Column | Description |
|---|---|
| Name | Container name (click to inspect) |
| Image | Image tag used by the container |
| State | Running, Stopped, Paused, Restarting, Created |
| Health | Health check status (if configured) |
| Uptime | Time since container started/stopped |
| CPU % | Real-time CPU usage percentage |
| Memory | Current memory usage |
| Network I/O | Received / Sent bytes |
| Disk I/O | Read / Write bytes |
| IP Address | Container's IP address |
| Ports | Published port mappings (host:container) |
| Stack | Compose project name (if part of a stack) |
Search and sort
Use the search box to filter containers by name, image, or stack. Click column headers to sort. Press Esc to clear the search.
Bulk operations
Select multiple containers using the checkboxes, then use the bulk action buttons:
- Start - Start all selected stopped containers
- Stop - Stop all selected running containers
- Restart - Restart all selected containers
- Remove - Delete all selected containers
Container actions
Each container row has action buttons for common operations:
| Action | Description |
|---|---|
| Start | Start a stopped container |
| Stop | Stop a running container (with confirmation) |
| Pause | Pause a running container |
| Restart | Restart a container (with confirmation) |
| Inspect | View detailed container configuration |
| Browse Files | Open file browser (running containers only) |
| Edit | Edit container configuration |
| Logs | Open logs panel |
| Terminal | Open interactive shell |
| Delete | Remove container (with confirmation) |
Creating containers
Click the Create container button to launch a new container. The modal provides all configuration options:
Basic settings
- Image - Select from local images or enter image:tag to pull
- Name - Container name (auto-generated if empty)
- Command - Override the default command
- Working directory - Set the working directory
Port mappings
Map container ports to host ports. Format: host_port:container_port/protocol
Port publishing binds container ports to host interfaces. See Docker networking documentation for details on port mapping and network modes.
Volume mounts
Mount host directories or named volumes into the container:
- Bind mount -
/host/path:/container/path - Named volume -
volume_name:/container/path - Read-only - Append
:rofor read-only access
Environment variables
Set environment variables as key-value pairs. Sensitive values are masked in the UI.
Network
Select the network mode:
- bridge (default) - Container gets its own IP on a bridge network
- host - Container shares the host's network stack
- none - No networking
- Custom network - Connect to a user-defined network
Restart policy
| Policy | Behavior |
|---|---|
no |
Never restart automatically |
always |
Always restart when stopped |
unless-stopped |
Restart unless manually stopped |
on-failure |
Restart only on non-zero exit code |
Resource limits
- CPU limit - Maximum CPU cores (e.g., 0.5 for half a core)
- Memory limit - Maximum memory (e.g., 512m, 1g)
Logs viewer
The logs viewer provides real-time streaming of container logs with ANSI color support.
Features
- Real-time streaming - Logs appear instantly via Server-Sent Events
- ANSI color rendering - Full color support for formatted output
- Pause/Resume - Toggle streaming to freeze the view
- Auto-scroll - Automatically scroll to the latest logs
- Download - Export logs as a text file
- Font size - Adjust between 10-18px
Connection status
The status indicator shows the current connection state:
- Live (green pulse) - Streaming active
- Connecting (yellow) - Establishing connection
- Disconnected (red) - Connection lost, will retry
- Paused (amber) - Streaming manually paused
The default log buffer is 500 KB per panel. You can adjust this in Settings > General > Log buffer size. Larger buffers may impact browser performance.
Terminal
Open an interactive terminal session inside a running container.
Configuration
- Shell - Select bash, sh, zsh, or ash
- User - Run as root, nobody, or container default
- Font size - Adjust terminal font size
Keyboard shortcuts
- Cmd + L - Clear terminal
- Cmd + C - Copy selection
- Cmd + V - Paste
File browser
Browse and manage files inside running containers.
Features
- Navigation - Browse directories with breadcrumb trail
- Download - Download files or directories as tar archive
- Upload - Upload files to the container
- File info - View file sizes, types, and permissions
Auto-update
Configure automatic image updates for containers to keep them running the latest version.
Schedule options
- Daily - Update once per day at a specific time
- Weekly - Update on a specific day and time
- Custom - Use a cron expression for precise scheduling
Vulnerability criteria
When vulnerability scanning is enabled, you can block updates based on scan results:
| Criteria | Behavior |
|---|---|
| Never block | Always update regardless of vulnerabilities |
| Any | Block if new image has any vulnerabilities |
| Critical or High | Block if critical or high severity vulnerabilities found |
| Critical only | Block only for critical severity vulnerabilities |
| More than current | Block if new image has more total vulnerabilities than current |
Auto-update pulls the latest image, stops the current container, and creates a new container with the same configuration. This causes brief downtime. For zero-downtime updates, use Docker Compose with rolling update strategies.
Compose stacks
Manage Docker Compose stacks for multi-container applications. Dockhand supports both manually created stacks and stacks deployed from Git repositories.
Stack types
| Type | Badge | Description |
|---|---|---|
| Internal | Blue (FileCode) | Created directly in Dockhand with YAML editor |
| Git Stack | Purple (GitBranch) | Synced from a Git repository with auto-update |
| External | Gray (ExternalLink) | Created outside Dockhand (e.g., via CLI) |
Creating stacks
Click Create stack to open the YAML editor:
- Enter a stack name (becomes the Compose project name)
- Write or paste your
docker-compose.ymlcontent - Optionally check "Deploy immediately" to start the stack
- Click Create
Git integration
Deploy stacks directly from Git repositories with automatic synchronization.
Triggers: Scheduled auto-sync • Webhook from CI/CD • Manual deploy button
Configuration
- Repository - Select from configured Git repositories or add a new one
- Branch - Target branch to track
- Compose file path - Path to the compose file (default:
docker-compose.yml) - Auto-sync - Enable automatic sync on a schedule
- Schedule - Cron expression for auto-sync timing
Deployment behavior
Git stacks use intelligent deployment:
- Only redeploys when the Git commit changes (detected via
git pull) - Uses
docker compose up -d --remove-orphanswhich only recreates changed services - Manual "Deploy" button forces redeployment regardless of changes
Webhooks
Trigger stack deployments from external services like GitHub, GitLab, or CI/CD pipelines.
Push to repo → webhook triggers → Dockhand pulls changes → deploys updated stack
Webhook URL
Each Git stack gets a unique webhook URL:
https://your-dockhand.com/api/git/stacks/{id}/webhook
Supported methods
- GET - Simple trigger (for testing)
- POST - Standard webhook with optional signature verification
Webhook secret
For security, configure a webhook secret. The request signature is validated using HMAC-SHA256 in the X-Hub-Signature-256 header (GitHub compatible).
In your GitHub repository, go to Settings > Webhooks > Add webhook. Paste the webhook URL, set content type to application/json, and enter your secret.
Images
The Images page shows all Docker images on the selected environment, grouped by repository.
Image actions
| Action | Description |
|---|---|
| Run | Create a new container from this image |
| Scan | Scan for vulnerabilities (if scanner configured) |
| Tag | Add a new tag to this image |
| Push | Push to a configured registry |
| Export | Download as tar/tar.gz archive |
| History | View image layer history |
| Delete | Remove the image |
Pulling images
Click Pull image to download a new image:
- Optionally select a registry (default: Docker Hub)
- Enter the image name (e.g.,
nginx,redis) - Enter the tag (e.g.,
latest,alpine) - Click Pull
Progress is shown layer-by-layer with download percentages.
Vulnerability scanning
Dockhand integrates with Grype and Trivy vulnerability scanners to identify security issues in your images.
Severity levels
| Severity | Color | Description |
|---|---|---|
| Critical | Red | Severe vulnerabilities requiring immediate attention |
| High | Orange | Important vulnerabilities to fix soon |
| Medium | Yellow | Moderate risk vulnerabilities |
| Low | Blue | Minor vulnerabilities |
Export formats
Scan results can be exported in multiple formats:
- Markdown - Human-readable report
- CSV - Spreadsheet format for analysis
- JSON - Raw data for automation
Scan caching
Scan results are cached by image SHA256. Re-scanning the same image version returns cached results unless you force a fresh scan.
Enable "Scan on pull" in environment settings to automatically scan images when they're pulled. This ensures all new images are checked for vulnerabilities.
Registry operations
Pushing images
Push images to configured registries. Select the target registry and optionally specify a new tag.
Image history
View the layer-by-layer build history of an image, including:
- Layer size
- Creation timestamp
- Command that created the layer
Volumes
Manage Docker volumes for persistent data storage.
Volume actions
- Browse - Open volume browser (uses helper container)
- Clone - Create a copy of volume configuration
- Inspect - View detailed volume information
- Export - Download volume contents as tar archive
- Delete - Remove the volume (fails if in use)
Volume browser
Browse the contents of Docker volumes using a helper container.
Volume browsing uses a busybox:latest helper container that mounts the volume read-only. The container is automatically pulled if not present and removed when you close the browser.
Networks
Manage Docker networks for container communication.
Network drivers
| Driver | Description |
|---|---|
bridge |
Default network for standalone containers |
host |
Container uses host's network stack |
overlay |
Multi-host networking (Swarm mode) |
macvlan |
Assign MAC address for direct network access |
none |
No networking |
Creating networks
Configure:
- Name - Network name
- Driver - Network driver type
- Subnet - CIDR notation (e.g.,
172.20.0.0/16) - Gateway - Gateway IP address
- Internal - Restrict external access
- Attachable - Allow manual container attachment
Connecting containers
Use the "Connect container" action to attach containers to a network with optional:
- Aliases - DNS aliases for the container on this network
- IPv4 address - Specific IP address assignment
The default bridge, host, and none networks cannot be deleted. They are marked with a "Built-in" badge.
Registry browser
Browse and search Docker registries to discover and pull images.
Features
- Search across configured registries
- View available tags for each image
- Pull images directly from search results
- View image details and manifests
Configure registries in Settings > Registries. Private registries require authentication credentials.
Activity log
Track all Docker container events across your environments in real-time.
Event types
| Event | Icon | Description |
|---|---|---|
create |
+ | Container created |
start |
Play (green) | Container started |
stop |
Stop (red) | Container stopped |
die |
Skull | Container exited |
kill |
X | Container killed |
restart |
Refresh | Container restarted |
pause |
Pause | Container paused |
unpause |
Play | Container unpaused |
oom |
Alert | Out of memory |
health_status |
Heart | Health check result |
Filtering
Filter the activity log by:
- Container name - Text search
- Event type - Select specific event types
- Environment - Filter by environment
- Labels - Filter by environment labels
- Date range - Presets or custom range
Activity collection must be enabled per-environment in Settings > Environments. The dashboard tile shows an amber "Activity" icon when collection is active.
Schedules
View and manage all scheduled jobs in one place.
Schedule types
| Type | Description |
|---|---|
| Container auto-update | Automatic image updates for containers |
| Git stack sync | Automatic sync and deploy from Git repositories |
| System cleanup | Built-in cleanup jobs (schedule execution logs, container events) |
Schedule management
- Enable/Disable - Toggle schedule without deleting
- Last run - When the schedule last executed
- Next run - Calculated next execution time
- Delete - Remove the schedule
System schedules
Dockhand includes built-in cleanup schedules that run automatically:
| Job | Description | Default retention |
|---|---|---|
| Schedule execution cleanup | Removes historical logs from auto-update and Git sync executions, including success/failure status and error messages | 30 days |
| Container event cleanup | Removes old container activity events (start, stop, restart, die) from the activity feed and dashboard timeline | 7 days |
Configure retention periods in Settings > General > Cleanup jobs.
Hawser remote agent
Hawser is a lightweight Go agent that enables Dockhand to manage Docker hosts in various network configurations, including hosts behind NAT, firewalls, or with dynamic IPs.
Connection modes
Hawser supports two operational modes:
| Mode | Use Case | How It Works |
|---|---|---|
| Standard | LAN, homelab, static IPs | Agent listens, Dockhand connects to it |
| Edge | VPS, NAT, dynamic IP, firewalls | Agent connects outbound to Dockhand via WebSocket |
Standard mode
Dockhand connects directly to the Hawser agent running on your Docker host. The agent exposes an HTTP API that proxies requests to the local Docker socket.
Pros
- Simple setup - just run the agent
- Lower latency (direct connection)
- Works on any network with IP connectivity
- Optional TLS encryption
Cons
- Requires inbound port access to Docker host
- Need static IP or DNS for the agent
- Firewall rules may be needed
Edge mode
The Hawser agent initiates an outbound WebSocket connection to Dockhand. All communication flows through this persistent connection - no inbound ports required on the Docker host.
Pros
- No inbound ports required
- Works behind NAT, firewalls, dynamic IPs
- Perfect for VPS and cloud instances
- Auto-reconnect with exponential backoff
Cons
- Dockhand must be publicly accessible (or via VPN)
- Slightly higher latency (WebSocket overhead)
- Token authentication is mandatory
Installation
Quick install script
The recommended way to install Hawser on Linux:
curl -fsSL https://raw.githubusercontent.com/Finsys/hawser/main/scripts/install.sh | bash
This script:
- Detects OS and architecture (Linux amd64/arm64)
- Downloads the latest release from GitHub
- Installs to
/usr/local/bin/hawser - Creates config directory at
/etc/hawser/ - Installs systemd service (or OpenRC on Alpine)
Docker installation
Standard mode:
docker run -d \
--name hawser \
--restart unless-stopped \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 2376:2376 \
-e TOKEN=your-secret-token \
ghcr.io/finsys/hawser:latest
Edge mode:
docker run -d \
--name hawser \
--restart unless-stopped \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DOCKHAND_SERVER_URL=wss://your-dockhand.example.com/api/hawser/connect \
-e TOKEN=your-agent-token \
-e AGENT_NAME=my-server \
ghcr.io/finsys/hawser:latest
Systemd service
Create /etc/systemd/system/hawser.service:
[Unit]
Description=Hawser - Remote Docker Agent for Dockhand
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
[Service]
Type=simple
ExecStart=/usr/local/bin/hawser
Restart=always
RestartSec=10
EnvironmentFile=/etc/hawser/config
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now hawser
Configuration
Configure Hawser using environment variables or a config file at /etc/hawser/config:
Standard mode config
# Standard Mode Configuration
DOCKER_SOCKET=/var/run/docker.sock
PORT=2376
TOKEN=your-secret-token
AGENT_NAME=my-server
LOG_LEVEL=info
# Optional TLS
# TLS_CERT=/etc/hawser/server.crt
# TLS_KEY=/etc/hawser/server.key
Edge mode config
# Edge Mode Configuration
DOCKER_SOCKET=/var/run/docker.sock
DOCKHAND_SERVER_URL=wss://your-dockhand.example.com/api/hawser/connect
TOKEN=your-agent-token-from-dockhand
AGENT_NAME=my-server
LOG_LEVEL=info
# Connection settings
HEARTBEAT_INTERVAL=30
RECONNECT_DELAY=1
MAX_RECONNECT_DELAY=60
Environment variables reference
| Variable | Default | Mode | Description |
|---|---|---|---|
DOCKHAND_SERVER_URL |
- | Edge | WebSocket URL for Dockhand connection |
TOKEN |
- | Both | Authentication token |
PORT |
2376 | Standard | HTTP server port |
DOCKER_SOCKET |
/var/run/docker.sock | Both | Docker socket path |
AGENT_NAME |
hostname | Both | Human-readable agent name |
LOG_LEVEL |
info | Both | debug, info, warn, error |
TLS_CERT |
- | Standard | TLS certificate path |
TLS_KEY |
- | Standard | TLS private key path |
CA_CERT |
- | Edge | CA certificate path (for self-signed Dockhand) |
HEARTBEAT_INTERVAL |
30 | Edge | Heartbeat interval in seconds |
TLS configuration
TLS certificate handling differs based on which side initiates the connection:
| Mode | Connection direction | TLS config location |
|---|---|---|
| Standard | Dockhand → Hawser | Dockhand UI (CA cert for self-signed) |
| Edge | Hawser → Dockhand | Hawser agent config (CA cert for self-signed Dockhand) |
Standard mode TLS
When Hawser uses a self-signed TLS certificate, configure the CA certificate in Dockhand:
- Go to Settings > Environments
- Edit the Hawser Standard environment
- Set protocol to HTTPS
- Paste the CA certificate in the CA certificate field
- Optionally enable Skip TLS verification for testing (insecure)
Edge mode TLS
When Dockhand uses a self-signed TLS certificate, configure the CA certificate on the Hawser agent:
# Edge mode with self-signed Dockhand certificate
DOCKHAND_SERVER_URL=wss://your-dockhand.example.com/api/hawser/connect
TOKEN=your-agent-token
CA_CERT=/etc/hawser/dockhand-ca.pem
# Or skip verification (insecure, for testing only)
# TLS_SKIP_VERIFY=true
The CA certificate must be on the client side to verify the server's identity. In Standard mode, Dockhand connects to Hawser (Dockhand is client). In Edge mode, Hawser connects to Dockhand (Hawser is client).
Token management
Edge mode requires tokens generated in Dockhand:
- Go to Settings > Environments
- Create or edit an environment with "Hawser - Edge" connection type
- Click Generate Token
- Copy the token immediately (shown only once)
- Configure Hawser on your Docker host with this token
Tokens are stored as Argon2id hashes in the database. The full token is shown only once at generation time. If lost, you must generate a new token.
Troubleshooting
Common issues
| Issue | Cause | Solution |
|---|---|---|
| "Unauthorized" error | Token mismatch | Verify token matches between agent and Dockhand |
| "Edge agent not connected" | Agent not running | Start agent, check DOCKHAND_SERVER_URL |
| "Docker socket not found" | Wrong socket path | Set DOCKER_SOCKET=/var/run/docker.sock |
| Connection timeout | Network/firewall | Check firewall rules, WebSocket support |
Debug mode
Enable detailed logging:
LOG_LEVEL=debug hawser
Check logs
# Systemd
sudo journalctl -u hawser -f
# Docker
docker logs -f hawser
Settings
Configure Dockhand behavior, environments, authentication, and integrations.
General settings
| Setting | Default | Description |
|---|---|---|
| Show stopped containers | On | Include stopped containers in lists |
| Time format | 24-hour | 12-hour or 24-hour time display |
| Date format | DD.MM.YYYY | Date display format |
| Download format | tar | Format for file exports (tar or tar.gz) |
| Confirm destructive actions | On | Show confirmations before delete operations |
| Log buffer size | 500 KB | Maximum log buffer per panel (100-5000 KB) |
Vulnerability scanner defaults
Configure default CLI arguments for vulnerability scanners. These settings allow you to adjust scanner behavior if Trivy or Grype CLI options change in future versions, or to add custom flags for your environment:
- Grype CLI args - Default:
-o json -v {image} - Trivy CLI args - Default:
image --format json {image}
The {image} placeholder is replaced with the actual image name at scan time.
Cleanup jobs
Configure automatic cleanup of old data to prevent database growth:
| Job | What it cleans | Default retention |
|---|---|---|
| Schedule execution cleanup | Removes historical logs from auto-update and Git sync executions. These logs track when scheduled tasks ran, their success/failure status, and any error messages. Older logs are typically not needed once reviewed. | 30 days |
| Container event cleanup | Removes old container activity events (start, stop, restart, die, etc.) from the activity feed and dashboard. These events are collected from Docker and stored for the activity timeline. High-traffic environments can generate thousands of events daily. | 7 days |
Each cleanup job can be enabled/disabled independently with configurable retention periods (1-365 days). Jobs run automatically on a daily schedule.
Environments
Manage Docker environment connections.
Connection types
| Type | Description | Use Case |
|---|---|---|
| Unix Socket | Local Docker socket | Dockhand on same host as Docker |
| Direct (HTTP/HTTPS) | Remote Docker API | Docker with exposed port |
| Hawser Standard | Hawser agent listens | LAN with static IPs |
| Hawser Edge | Hawser agent connects out | VPS, NAT, dynamic IPs |
Environment options
- Name - Display name for the environment
- Icon - Custom icon from icon library
- Labels - Tags for filtering (with colors)
- Default environment - Selected by default on page load
- Collect metrics - Enable CPU/memory monitoring
- Collect activity - Track container events
- Highlight changes - Visual indicators for stat changes
Vulnerability scanning
Per-environment scanner configuration:
- Scanner - None, Grype, Trivy, or Both
- Scan on pull - Auto-scan pulled images
- Custom CLI args - Override default scanner arguments
Registries
Configure Docker registries for image operations.
- Name - Display name
- URL - Registry endpoint
- Username/Password - Authentication credentials
- Default - Set as default for pull operations
Git
Manage Git credentials and repositories for stack deployment.
Credentials
- SSH keys - For SSH authentication
- HTTPS credentials - Username/password or token
Repositories
- URL - Git repository URL
- Branch - Default branch to track
- Credential - Link to saved credential
Config sets
Config sets are reusable configuration templates that can be applied when creating containers. They help maintain consistency and save time when deploying multiple containers with similar configurations.
What's included in a config set
- Environment variables - Predefined key-value pairs
- Labels - Container labels for organization and filtering
- Port mappings - Common port configurations
- Volume mounts - Standard volume configurations
- Network mode - Network settings (bridge, host, etc.)
- Restart policy - Container restart behavior
Using config sets
- Go to Settings > Config sets
- Click Add config set
- Enter a name and configure the desired options
- Save the config set
- When creating a new container, select the config set from the dropdown to pre-fill values
Values from config sets are copied when creating a container. Changes to a config set won't affect existing containers.
Notifications
Configure notification channels for alerts.
Channel types
- SMTP - Email notifications via SMTP server
- Apprise - Webhooks for Discord, Slack, Telegram, ntfy, Gotify, Pushover, etc.
Event types
Configure which events trigger notifications:
- Container events - start, stop, unhealthy, OOM
- Auto-update events - success, failed, blocked
- Git stack events - sync success, failed, skipped
- Stack events - deployed, failed
- Security events - vulnerabilities found
- System events - environment offline, disk warning
Authentication
Configure user authentication and access control.
General settings
- Enable authentication - Toggle auth for the application
- Session timeout - Session expiration (1-7 days)
Local users
Create and manage local user accounts:
- Username, display name, email
- Password (Argon2id hashed)
- Admin role toggle
- Avatar upload
- Enable/disable account
OIDC/SSO
Configure Single Sign-On with OpenID Connect providers.
Supported providers
- Microsoft Azure AD
- Okta
- Keycloak
- Auth0
- Any OIDC-compliant provider
Configuration
- Name - Provider display name
- Issuer URL - OIDC discovery endpoint
- Client ID - OAuth client ID
- Client secret - OAuth client secret
- Scopes - OAuth scopes (default: openid profile email)
See the OIDC Configuration Guide for detailed setup instructions with Keycloak and other providers.
LDAP/Active Directory Enterprise
Connect to LDAP or Active Directory for user authentication.
LDAP/Active Directory integration requires an Enterprise license.
Connection settings
- Server URL - LDAP server (e.g.,
ldap://ldap.example.com:389) - Bind DN - Service account DN for searching
- Bind password - Service account password
- Base DN - Search base (e.g.,
dc=example,dc=com)
User search
- User filter - LDAP filter template
- OpenLDAP:
(uid={{username}}) - Active Directory:
(sAMAccountName={{username}})
- OpenLDAP:
- Username attribute - Attribute for username (uid, sAMAccountName)
- Email attribute - Attribute for email (mail)
- Display name attribute - Attribute for display name (cn)
Group settings
- Group base DN - Where to search for groups
- Admin group - Group DN for admin access
- Member filter - Filter to find user's groups
TLS settings
- Enable TLS - Use LDAPS or StartTLS
- CA certificate - Custom CA for verification
See the LDAP/AD Configuration Guide for detailed setup instructions.
Roles (RBAC) Enterprise
Configure role-based access control for granular permissions.
System roles
- Admin - Full access to everything
- Operator - Manage containers, images, stacks
- Viewer - Read-only access
Custom roles
Create roles with granular permissions:
- Containers - view, create, edit, start/stop, remove, exec, logs
- Images - view, pull, remove
- Volumes - view, create, remove, browse
- Networks - view, create, remove
- Stacks - view, create, edit, deploy, remove
- And more...
Environment scoping
Custom roles can be scoped to specific environments:
- All environments - Role applies everywhere
- Specific environments - Role only applies to selected environments
License
Activate and manage your Enterprise license.
Enterprise features
An Enterprise license unlocks:
- Role-based access control (RBAC)
- LDAP/Active Directory integration
- Audit logging
- Priority support
Activation
- Enter your license name (customer name)
- Enter your license key
- Click Activate
The license is validated against:
- Customer name
- Hostname (or wildcard)
- Expiration date
- Cryptographic signature
Enterprise features
Enterprise edition adds advanced security and compliance features for organizations.
Role-based access control
RBAC provides fine-grained control over who can access what:
- Users can be assigned multiple roles
- Roles define permissions for resources and actions
- Environment scoping limits role access to specific environments
Free vs Enterprise
| Feature | Free Edition | Enterprise |
|---|---|---|
| Authentication | Yes (SSO + local users) | Yes |
| Permissions | All users have full access | Granular RBAC |
| Environment scoping | No | Yes |
Audit logging
Track all user actions for compliance and security auditing.
Audit logs capture:
- Who performed the action (user)
- What action was performed (create, update, delete, start, stop, etc.)
- What resource was affected (container, image, stack, etc.)
- When the action occurred (timestamp)
- Additional context (IP address, user agent)
Multi-factor authentication
Add an extra layer of security with TOTP-based MFA. This feature is available in both free and enterprise edition.
Setup process
- Go to your Profile page
- Click Enable MFA
- Scan the QR code with an authenticator app (Google Authenticator, Authy, etc.)
- Enter the verification code
- Save your backup codes securely
Store your backup codes in a safe place. You can use them to access your account if you lose your authenticator device.
Keyboard shortcuts
Global
| Shortcut | Action |
|---|---|
| Esc | Close modal / Clear search |
| ⌘ + K / Ctrl + K | Open command palette - quick navigation and actions |
Terminal
| Shortcut | Action |
|---|---|
| Cmd + L | Clear terminal |
| Cmd + C | Copy selection |
| Cmd + V | Paste |
Troubleshooting
Dockhand won't start
- Check Docker socket is accessible:
docker ps - Verify port 5173 is not in use
- Check container logs:
docker logs dockhand
Can't connect to environment
- Verify Docker is running on the target host
- Check firewall rules allow connection
- For Hawser: verify agent is running and token is correct
Locked out of authentication
Use emergency scripts to regain access:
Emergency scripts
Recovery scripts are located in /app/scripts/emergency/ inside the container:
# Disable authentication
docker exec -it dockhand /app/scripts/emergency/disable-auth.sh
# Create admin user (admin/admin123)
docker exec -it dockhand /app/scripts/emergency/create-admin.sh
# Reset a user's password
docker exec -it dockhand /app/scripts/emergency/reset-password.sh username NewPassword123
# List all users
docker exec -it dockhand /app/scripts/emergency/list-users.sh
# Clear all sessions (force re-login)
docker exec -it dockhand /app/scripts/emergency/clear-sessions.sh
# Backup database
docker exec -it dockhand /app/scripts/emergency/backup-db.sh /app/data/backups
Emergency scripts require shell access to the container. In production, restrict access to the Docker socket and container.
API reference
Dockhand exposes a REST API that can be used for automation and integration with other tools. All endpoints are available at /api/.
When authentication is enabled, API requests must include a valid session cookie. Use the same authentication as the web UI.
Containers
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/containers |
List all containers |
POST |
/api/containers |
Create a new container |
GET |
/api/containers/[id] |
Get container details |
DELETE |
/api/containers/[id] |
Remove container |
POST |
/api/containers/[id]/start |
Start container |
POST |
/api/containers/[id]/stop |
Stop container |
POST |
/api/containers/[id]/restart |
Restart container |
GET |
/api/containers/[id]/logs |
Get container logs |
Stacks
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/stacks |
List all compose stacks |
POST |
/api/stacks |
Create a new stack |
POST |
/api/stacks/[name]/start |
Start stack |
POST |
/api/stacks/[name]/stop |
Stop stack |
DELETE |
/api/stacks/[name] |
Remove stack |
Images
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/images |
List all images |
POST |
/api/images/pull |
Pull an image from registry |
DELETE |
/api/images/[id] |
Remove image |
POST |
/api/images/scan |
Scan image for vulnerabilities |
Volumes & Networks
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/volumes |
List all volumes |
POST |
/api/volumes |
Create a volume |
DELETE |
/api/volumes/[name] |
Remove volume |
GET |
/api/networks |
List all networks |
POST |
/api/networks |
Create a network |
DELETE |
/api/networks/[id] |
Remove network |
Other endpoints
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/activity |
Get activity log |
GET |
/api/environments |
List environments |
GET |
/api/dashboard/stats |
Get dashboard statistics |
GET |
/api/schedules |
List scheduled tasks |
Query parameters
Most endpoints accept an env query parameter to specify the target environment:
GET /api/containers?env=1
GET /api/images?env=2
Example: Using curl
# List containers
curl -s http://localhost:3000/api/containers | jq
# Restart a container
curl -X POST http://localhost:3000/api/containers/abc123/restart
# Pull an image
curl -X POST http://localhost:3000/api/images/pull \
-H "Content-Type: application/json" \
-d '{"image": "nginx:latest"}'
# Create a stack
curl -X POST http://localhost:3000/api/stacks \
-H "Content-Type: application/json" \
-d '{"name": "mystack", "compose": "version: \"3\"\\nservices:\\n web:\\n image: nginx"}'
Appendix: OIDC/SSO configuration guide
This guide explains how to configure OpenID Connect (OIDC) Single Sign-On in Dockhand.
Overview
Dockhand supports OIDC authentication, allowing users to log in using external identity providers:
- Microsoft Azure AD
- Okta
- Keycloak
- Auth0
- Any OIDC-compliant provider
Keycloak setup example
1. Create a realm
- Log in to Keycloak Admin Console
- Create a new realm (e.g.,
dockhand)
2. Create a client
- Go to Clients > Create client
- Configure:
- Client ID:
dockhand-app - Client type: OpenID Connect
- Client authentication: ON (for confidential client)
- Client ID:
- Click Next and configure:
- Valid redirect URIs:
https://your-domain/api/auth/oidc/callback - Web origins:
https://your-domain
- Valid redirect URIs:
- Click Save
- Go to Credentials tab and copy the Client secret
3. Configure group mapper
To pass group membership to Dockhand for role mapping:
- Go to Clients > dockhand-app > Client scopes
- Click on dockhand-app-dedicated scope
- Click Add mapper > By configuration > Group Membership
- Configure:
- Name:
groups - Token Claim Name:
groups - Full group path: OFF
- Add to ID token: ON
- Add to access token: ON
- Add to userinfo: ON
- Name:
- Click Save
4. Create groups
Create groups in Keycloak that map to Dockhand roles:
- Go to Groups > Create group
- Create groups such as:
dockhand-admins- for administrator accessdockhand-operators- for operator accessdockhand-viewers- for read-only access
5. Assign users to groups
- Go to Users > [select user] > Groups
- Click Join Group
- Select the appropriate group(s)
Dockhand configuration
1. Enable Enterprise license
Role mapping features require an Enterprise license:
- Go to Settings > License
- Enter your license name and key
- Click Activate
2. Configure OIDC provider
- Go to Settings > Auth > SSO
- Click Add provider
- Fill in the basic settings:
| Field | Example Value |
|---|---|
| Name | Keycloak |
| Issuer URL | https://keycloak.example.com/realms/dockhand |
| Client ID | dockhand-app |
| Client secret | (from Keycloak) |
| Redirect URI | https://your-domain/api/auth/oidc/callback |
| Scopes | openid profile email |
3. Configure claim mappings (optional)
| Field | Default | Description |
|---|---|---|
| Username claim | preferred_username |
Claim for username |
| Email claim | email |
Claim for email address |
| Display name claim | name |
Claim for display name |
4. Configure admin mapping
Grant admin access based on group membership:
- Edit the OIDC provider
- Scroll to Groups/roles claim section
- Configure:
- Claim name:
groups - Admin value(s):
dockhand-admins
- Claim name:
Users with dockhand-admins in their groups claim will automatically receive Admin role.
5. Configure role mappings (Enterprise)
Map additional groups to Dockhand roles:
- Edit the OIDC provider
- Scroll to Claim to role mappings section
- Click Add mapping
- For each mapping, configure:
- Claim name:
groups - Claim value: e.g.,
dockhand-operators - Role: Select from dropdown
- Claim name:
| Keycloak Group | Dockhand Role |
|---|---|
dockhand-admins |
Admin (via admin mapping) |
dockhand-operators |
Operator |
dockhand-viewers |
Viewer |
Other identity providers
Issuer URL: https://accounts.google.com
Scopes: openid profile email
Note: Google doesn't support custom claims for role mapping. All Google users will need manual role assignment.
Azure AD
Issuer URL: https://login.microsoftonline.com/{tenant-id}/v2.0
Scopes: openid profile email
For role mapping, configure Azure AD to include group claims in the token.
Okta
Issuer URL: https://{your-domain}.okta.com
Scopes: openid profile email groups
Configure a groups claim in the Authorization Server.
Troubleshooting OIDC
User has no permissions after login
- Verify the user is assigned to groups in your IdP
- Check that the groups mapper is configured correctly
- Ensure Add to ID token is enabled for the groups mapper
- Verify the claim name matches in Dockhand config (case-sensitive)
Admin mapping not working
- Verify the admin claim name matches exactly (case-sensitive)
- Check that the admin value matches the group name exactly
- Ensure the user is a member of the admin group in your IdP
Redirect URI mismatch error
- Ensure the redirect URI in Dockhand matches exactly what's configured in your IdP
- Check for trailing slashes
- Verify the protocol (http vs https) matches
Token claims not appearing
For Keycloak:
- Go to Clients > [client] > Client scopes
- Verify mappers are configured
- Use Keycloak's Evaluate feature to test token contents:
- Go to Clients > [client] > Client scopes > Evaluate
- Select a user and click Generated ID token
- Verify the
groupsclaim is present
- Use HTTPS in production - Never use HTTP for OIDC in production environments
- Protect client secrets - Store client secrets securely, never commit to version control
- Validate redirect URIs - Only allow specific, known redirect URIs
- Use short token lifetimes - Configure appropriate token expiration in your IdP
- Regular audits - Review OIDC user access and role mappings periodically
Appendix: LDAP/Active Directory configuration guide
This guide explains how to configure LDAP or Active Directory authentication in Dockhand.
LDAP/Active Directory integration requires an Enterprise license.
Supported directories
- OpenLDAP
- Microsoft Active Directory
- FreeIPA
- 389 Directory Server
- Any LDAP v3 compliant directory
How LDAP authentication works
- User enters username and password in Dockhand
- Dockhand connects to LDAP server using bind credentials (service account)
- Searches for the user entry using the configured filter
- Attempts to bind as the found user with the provided password
- If successful, retrieves user attributes (email, display name)
- Optionally checks group membership for admin/role assignment
- Creates or updates user in Dockhand and establishes session
Key terminology
| Term | Description |
|---|---|
| DN (Distinguished Name) | Unique identifier for an entry, e.g., cn=john,ou=users,dc=example,dc=com |
| Base DN | Starting point for searches, e.g., dc=example,dc=com |
| Bind DN | Service account DN used to search the directory |
| Filter | LDAP query to find entries, e.g., (uid=john) |
| Attribute | Property of an entry, e.g., mail, cn, uid |
OpenLDAP configuration
Example directory structure
dc=example,dc=com
├── ou=users
│ ├── uid=john (cn=John Doe, mail=john@example.com)
│ └── uid=jane (cn=Jane Smith, mail=jane@example.com)
└── ou=groups
├── cn=dockhand-admins (member: uid=john,ou=users,dc=example,dc=com)
└── cn=dockhand-users (member: uid=jane,ou=users,dc=example,dc=com)
Dockhand configuration for OpenLDAP
| Field | Value |
|---|---|
| Name | Corporate OpenLDAP |
| Server URL | ldap://ldap.example.com:389 |
| Bind DN | cn=readonly,dc=example,dc=com |
| Bind Password | (service account password) |
| Base DN | ou=users,dc=example,dc=com |
| User filter | (uid={{username}}) |
| Username attribute | uid |
| Email attribute | mail |
| Display name attribute | cn |
| Group base DN | ou=groups,dc=example,dc=com |
| Admin group | cn=dockhand-admins,ou=groups,dc=example,dc=com |
Active Directory configuration
Example AD structure
DC=corp,DC=example,DC=com
├── OU=Users
│ ├── CN=John Doe (sAMAccountName=jdoe, mail=jdoe@example.com)
│ └── CN=Jane Smith (sAMAccountName=jsmith, mail=jsmith@example.com)
└── OU=Groups
├── CN=Dockhand Admins
└── CN=Dockhand Users
Dockhand configuration for Active Directory
| Field | Value |
|---|---|
| Name | Corporate AD |
| Server URL | ldap://dc01.corp.example.com:389 |
| Bind DN | CN=Service Account,OU=Service Accounts,DC=corp,DC=example,DC=com |
| Base DN | OU=Users,DC=corp,DC=example,DC=com |
| User filter | (sAMAccountName={{username}}) |
| Username attribute | sAMAccountName |
| Email attribute | mail |
| Display name attribute | displayName |
| Group base DN | OU=Groups,DC=corp,DC=example,DC=com |
| Admin group | CN=Dockhand Admins,OU=Groups,DC=corp,DC=example,DC=com |
Alternative AD user filters
# By sAMAccountName (most common)
(sAMAccountName={{username}})
# By userPrincipalName (email-style login)
(userPrincipalName={{username}})
# By both (allow either format)
(|(sAMAccountName={{username}})(userPrincipalName={{username}}))
# Only enabled accounts
(&(sAMAccountName={{username}})(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
Group membership filters
# OpenLDAP with groupOfNames
(&(objectClass=groupOfNames)(member={{user_dn}}))
# OpenLDAP with posixGroup
(&(objectClass=posixGroup)(memberUid={{username}}))
# Active Directory
(&(objectClass=group)(member={{user_dn}}))
The {{user_dn}} placeholder is replaced with the user's full DN.
Role mappings (Enterprise)
Map LDAP groups to Dockhand roles:
- Click Add mapping
- Enter the full group DN
- Select the Dockhand role
| LDAP Group DN | Dockhand Role |
|---|---|
cn=dockhand-admins,ou=groups,dc=example,dc=com |
Admin (via admin group) |
cn=dockhand-operators,ou=groups,dc=example,dc=com |
Operator |
cn=dockhand-viewers,ou=groups,dc=example,dc=com |
Viewer |
TLS/SSL configuration
Server URL formats:
ldap://hostname:389- Plain LDAP (port 389)ldaps://hostname:636- LDAP over SSL (port 636)ldap://hostname:389with TLS enabled - StartTLS
For self-signed certificates, add the CA certificate in TLS settings.
Troubleshooting LDAP
Connection refused
- Verify LDAP server is running and accessible
- Check firewall rules (ports 389/636)
- Test connectivity:
telnet ldap.example.com 389
Invalid credentials (Bind DN)
- Verify Bind DN is correct and complete
- Check password is correct
- Ensure service account is not locked/disabled
User not found
- Verify Base DN includes the user's location
- Test user filter manually with
ldapsearch:
# OpenLDAP
ldapsearch -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" \
-W \
-b "ou=users,dc=example,dc=com" \
"(uid=john)"
# Active Directory
ldapsearch -x -H ldap://dc01.corp.example.com:389 \
-D "CN=Service Account,DC=corp,DC=example,DC=com" \
-W \
-b "DC=corp,DC=example,DC=com" \
"(sAMAccountName=jdoe)"
User logs in but has no permissions
- Verify user is member of appropriate LDAP group
- Check group DN matches exactly (case-sensitive)
- Verify member filter is correct for your directory type
- Ensure role mappings are configured
TLS/SSL certificate errors
- For self-signed certificates, add the CA certificate in TLS settings
- Verify server hostname matches certificate CN/SAN
- Check certificate is not expired
- Use TLS in production - Never send credentials over plain LDAP in production
- Principle of least privilege - Bind account should only have read access
- Protect bind credentials - Store bind password securely
- Audit group membership - Regularly review who has admin access
- Monitor failed logins - Watch for brute force attempts