A production-ready OpenSSH server Docker image based on Debian Bookworm, designed for secure remote access, development environments, and CI/CD pipelines.
- Secure by Default: Password authentication disabled, public key authentication preferred
- Flexible User Management: Customizable UID/GID for seamless host volume permissions
- Persistent Configuration: All configs and host keys stored in
/appvolume - Development Tools: Includes git, golang, curl, wget, and common utilities
- Timestamped Logging: Built-in log management with timestamps
- Easy SSH Key Management: Multiple ways to inject public keys
- Optional Sudo Access: Configurable sudo permissions
- Small Footprint: Based on Debian Bookworm Slim
# Pull the image docker pull ghcr.io/optimode/openssh:bookworm # Create config directory mkdir -p ./config/.ssh # Add your SSH public key cat ~/.ssh/id_rsa.pub > ./config/.ssh/authorized_keys # Run the container docker run -d \ --name openssh \ -p 2222:2222 \ -v ./config:/app \ -e USER_NAME=devuser \ -e PUID=1000 \ -e PGID=1000 \ ghcr.io/optimode/openssh:bookworm # Connect ssh -p 2222 devuser@localhost
Create a docker-compose.yml file:
services: openssh: image: ghcr.io/optimode/openssh:bookworm container_name: openssh restart: unless-stopped ports: - "2222:2222" volumes: - ./config:/app - ./workspace:/workspace environment: - TZ=Europe/Budapest - PUID=1000 - PGID=1000 - USER_NAME=devuser - SUDO_ACCESS=false - PASSWORD_ACCESS=false
Then run:
# Add your SSH public key mkdir -p ./config/.ssh cat ~/.ssh/id_rsa.pub > ./config/.ssh/authorized_keys # Start the container docker-compose up -d # Connect ssh -p 2222 devuser@localhost
| Variable | Default | Description |
|---|---|---|
USER_NAME |
app |
Username for SSH access |
PUID |
911 |
User ID (useful for matching host user permissions) |
PGID |
911 |
Group ID (useful for matching host group permissions) |
USER_HOME |
/app |
User home directory |
USER_PASSWORD |
<random> |
User password (auto-generated if not set) |
USER_PASSWORD_FILE |
- | Path to file containing password (Docker secrets) |
| Variable | Default | Description |
|---|---|---|
LISTEN_PORT |
2222 |
SSH server listening port |
PASSWORD_ACCESS |
false |
Enable/disable password authentication |
PUBLIC_KEY |
- | SSH public key (single key as string) |
PUBLIC_KEY_FILE |
- | Path to file containing a public key |
PUBLIC_KEY_DIR |
- | Path to directory containing multiple key files |
| Variable | Default | Description |
|---|---|---|
SUDO_ACCESS |
false |
Grant sudo privileges to the user |
UMASK |
022 |
Default umask for file creation |
| Variable | Default | Description |
|---|---|---|
TZ |
UTC |
Timezone (e.g., Europe/Budapest, America/New_York) |
| Path | Purpose |
|---|---|
/app |
Required - Persistent storage for SSH config, host keys, and user data |
/workspace |
Optional - Additional workspace directory |
| Port | Protocol | Description |
|---|---|---|
2222 |
TCP | SSH server port (configurable via LISTEN_PORT) |
There are multiple ways to provide SSH public keys for authentication:
mkdir -p ./config/.ssh cat ~/.ssh/id_rsa.pub > ./config/.ssh/authorized_keys chmod 600 ./config/.ssh/authorized_keys
environment: - PUBLIC_KEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAB...
environment: - PUBLIC_KEY_FILE=/secrets/ssh-key.pub volumes: - ./my-key.pub:/secrets/ssh-key.pub:ro
environment: - PUBLIC_KEY_DIR=/secrets/keys volumes: - ./keys:/secrets/keys:ro
Multiple methods can be used simultaneously - all keys will be added to authorized_keys.
services: devssh: image: optimode/openssh:bookworm container_name: devssh ports: - "2222:2222" volumes: - ./config:/app - ./projects:/workspace - /var/run/docker.sock:/var/run/docker.sock # Docker-in-Docker environment: - USER_NAME=developer - PUID=1000 - PGID=1000 - SUDO_ACCESS=true - PASSWORD_ACCESS=false - TZ=America/New_York
services: openssh: image: optimode/openssh:bookworm container_name: openssh-pass ports: - "2222:2222" volumes: - ./config:/app environment: - USER_NAME=testuser - USER_PASSWORD=SecurePassword123 - PASSWORD_ACCESS=true - SUDO_ACCESS=false
services: openssh: image: optimode/openssh:bookworm container_name: openssh-secure ports: - "2222:2222" volumes: - ./config:/app secrets: - ssh_password environment: - USER_NAME=admin - USER_PASSWORD_FILE=/run/secrets/ssh_password - PASSWORD_ACCESS=true secrets: ssh_password: file: ./secrets/password.txt
services: openssh: image: optimode/openssh:bookworm container_name: openssh-custom ports: - "2200:2200" volumes: - ./config:/app environment: - LISTEN_PORT=2200 - USER_NAME=sshuser - TZ=Asia/Tokyo - PUID=1001 - PGID=1001
# Clone the repository git clone https://github.com/optimode/docker-openssh.git cd docker-openssh # Build the image ./build # Or use docker build directly docker build -t optimode/openssh:bookworm .
/app/
├── .ssh/
│ └── authorized_keys # SSH public keys
├── sshd/
│ └── sshd_config # OpenSSH server configuration
├── ssh_host_keys/ # Persistent host keys
└── logs/
└── openssh/
└── sshd.log # SSH server logs with timestamps
Logs are stored in /app/logs/openssh/sshd.log with timestamps in format [YYYY-MM-DD HH:MM:SS TZ].
View logs:
# Real-time logs docker logs -f openssh # Persistent log file docker exec openssh tail -f /app/logs/openssh/sshd.log
Problem: Cannot connect to SSH server
Solutions:
- Check if container is running:
docker ps - Verify port mapping:
docker port openssh - Check firewall rules on host
- Verify SSH service is running:
docker exec openssh ps aux | grep sshd
Problem: Authentication fails with public key
Solutions:
- Verify key is in
authorized_keys:docker exec openssh cat /app/.ssh/authorized_keys - Check key permissions: Should be 600 for
authorized_keys, 700 for.ssh - Ensure the correct private key is being used:
ssh -i ~/.ssh/id_rsa -p 2222 user@localhost - Check logs:
docker logs openssh
Problem: Files in mounted volumes have wrong ownership
Solutions:
- Set
PUIDandPGIDto match your host user:id -u # Get your UID id -g # Get your GID
- Update docker-compose.yml with correct values
- Restart container:
docker-compose down && docker-compose up -d
Problem: User cannot write to /workspace directory
Solutions:
- Ensure proper ownership of host directory:
sudo chown -R 1000:1000 ./workspace # Use your PUID:PGID - Or set permissions to allow write access:
chmod -R 755 ./workspace
Problem: Sudo commands fail despite SUDO_ACCESS=true
Solutions:
- Verify environment variable is set:
docker exec openssh env | grep SUDO - Check sudoers file:
docker exec openssh cat /etc/sudoers | grep app - If password required, ensure
USER_PASSWORDis set - Restart container after changing
SUDO_ACCESS
- Disable Password Authentication: Always use key-based authentication in production
- Use Strong Keys: Generate keys with
ssh-keygen -t ed25519orssh-keygen -t rsa -b 4096 - Limit Sudo Access: Only enable
SUDO_ACCESS=truewhen necessary - Firewall Rules: Restrict SSH port access to known IPs
- Regular Updates: Keep the image updated for security patches
- Monitor Logs: Regularly check
/app/logs/openssh/sshd.logfor suspicious activity - Use Docker Secrets: For sensitive data like passwords, use Docker secrets instead of environment variables
- Base Image: debian:bookworm-slim
- OpenSSH Version: 8.4+
- Exposed Port: 2222 (default)
MIT License - See LICENSE file for details
Contributions are welcome! Please feel free to submit a Pull Request.
Optimode (Laszlo Malina)
- GitHub: https://github.com/optimode/openssh
- Docker Hub: https://hub.docker.com/r/optimode/openssh
- Issues: https://github.com/optimode/openssh/issues