This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Dockerfile Breakdown
Relevant source files
Purpose and Scope
This page provides a line-by-line analysis of the Dockerfile that constructs the docker-sshfs container image. The Dockerfile builds a complete SSHFS/Samba bridge server starting from a base Ubuntu image, installing necessary packages, configuring users and permissions, and setting up the critical filesystem integration that enables remote SSH filesystems to be accessed via SMB on macOS.
For runtime directory structure details, see Filesystem Layout. For Samba configuration specifics, see Samba Configuration (smb.conf)). For FUSE settings, see FUSE Configuration. For how the container starts and runs, see Service Lifecycle.
Sources: Dockerfile:1-34
Build Process Overview
The Dockerfile consists of distinct build stages that layer functionality progressively. Each RUN command creates a new image layer that contributes to the final container.
graph TD
BaseImage["ubuntu:latest\n(Base Layer)"]
PackageLayer["apt-get update && install\nsshfs + samba\n(Package Layer)"]
UserLayer["useradd sshuser\necho sshuser:sshpass\n(User Layer)"]
DirLayer["mkdir /remote\nmkdir /samba-share\n(Directory Layer)"]
ConfigLayer["COPY smb.conf\n(Configuration Layer)"]
PermLayer["chmod 777 /samba-share\n(Permissions Layer)"]
FuseLayer["echo user_allow_other >> fuse.conf\n(FUSE Layer)"]
ExposeLayer["EXPOSE 139 445\n(Port Declaration)"]
LinkLayer["ln -s /remote /samba-share/remote\n(Integration Layer)"]
CmdLayer["CMD smbd --foreground\n(Startup Layer)"]
BaseImage --> PackageLayer
PackageLayer --> UserLayer
UserLayer --> DirLayer
DirLayer --> ConfigLayer
ConfigLayer --> PermLayer
PermLayer --> FuseLayer
FuseLayer --> ExposeLayer
ExposeLayer --> LinkLayer
LinkLayer --> CmdLayer
Dockerfile Build Layers
Each layer builds on the previous, with dependencies flowing from top to bottom. The symbolic link layer is the final filesystem modification before container startup.
Sources: Dockerfile:2-33
Base Image Selection
Dockerfile2 specifies ubuntu:latest as the base image. Ubuntu is chosen because:
- Provides mature, well-tested FUSE implementation
- Includes
apt-getpackage manager with comprehensive SSHFS and Samba packages - Widely used base with extensive community support
- Contains necessary Linux kernel features for FUSE mounts
The latest tag automatically pulls the most recent Ubuntu LTS release, ensuring up-to-date packages and security patches.
Sources: Dockerfile2
Package Installation Layer
Dockerfile:5-6 performs a single-layer installation of both core packages:
| Package | Purpose | Provides |
|---|---|---|
sshfs | Remote filesystem client | SSHFS binary, FUSE dependencies, SSH client |
samba | SMB server | smbd daemon, SMB protocol implementation |
graph LR
AptGet["apt-get install"]
subgraph "sshfs Package"
SSHFSBin["sshfs binary"]
FuseDeps["libfuse2/libfuse3"]
SSHClient["openssh-client"]
end
subgraph "samba Package"
SMBDBin["smbd binary"]
SMBLibs["libsmbclient"]
SMBConf["Default /etc/samba/smb.conf"]
end
AptGet --> SSHFSBin
AptGet --> FuseDeps
AptGet --> SSHClient
AptGet --> SMBDBin
AptGet --> SMBLibs
AptGet --> SMBConf
SSHFSBin --> FuseDeps
SSHFSBin --> SSHClient
SMBDBin --> SMBLibs
The packages are installed in a single RUN command to minimize layer count. The -y flag auto-accepts prompts, enabling non-interactive installation.
Package Dependencies
The sshfs package automatically pulls FUSE userspace libraries and SSH client components. The samba package installs the smbd daemon along with protocol libraries.
Sources: Dockerfile:5-6
User Account Creation
Dockerfile9 creates a non-root user account with specific credentials:
- Username:
sshuser - Password:
sshpass - Home Directory:
/home/sshuser(created via-mflag)
Why Non-Root User
The sshuser account is critical for two reasons:
-
SSHFS Mount Ownership: SSHFS mounts are owned by the user who initiates the mount. Running SSHFS as
sshuserensures consistent file ownership (UID 1000, GID 1000). -
Samba Force User: The
smb.confforces all SMB operations to run assshuser, ensuring that file permissions match between SSHFS and Samba regardless of how macOS authenticates.
This account is used for interactive operations (via docker exec -it) but not for running the smbd daemon itself, which runs as root but operates on behalf of sshuser due to the force user directive.
Sources: Dockerfile9
Directory Structure Setup
Dockerfile12 and Dockerfile15 create the two critical directories in the container's filesystem:
| Directory | Purpose | Mount Role |
|---|---|---|
/remote | SSHFS mount point | Target for sshfs user@host:path /remote |
/samba-share | Samba root directory | Served as SMB share; contains symlink to /remote |
graph TB
RemoteDir["/remote\n(SSHFS mount target)"]
SambaDir["/samba-share\n(Samba root)"]
SymLink["/samba-share/remote\n(symbolic link)"]
RemoteDir -.->|linked by| SymLink
SambaDir -->|contains| SymLink
SSHFSMount["sshfs command\n(runtime)"]
SMBDServe["smbd daemon\n(serving)"]
SSHFSMount -->|mounts to| RemoteDir
SMBDServe -->|serves| SambaDir
These directories are initially empty. The /remote directory receives the SSHFS mount at runtime, while /samba-share is immediately accessible to Samba.
Directory Relationship
The symbolic link (created later in the Dockerfile) connects these directories, enabling files mounted at /remote to be accessible via /samba-share/remote.
Sources: Dockerfile12 Dockerfile15
Configuration File Integration
Dockerfile18 copies the custom Samba configuration file from the build context into the container. This replaces the default smb.conf installed by the samba package.
The custom configuration includes:
- Guest access enabled (
map to guest = Bad User) - Force user directive (
force user = sshuser) - Protocol restrictions (minimum SMB2)
- Share definition for
/samba-share
The build fails if smb.conf is not present in the build context. For complete configuration details, see Samba Configuration (smb.conf)).
Sources: Dockerfile18
Permission Configuration
Dockerfile:20-21 sets permissions on the Samba share directory. Note that line 20 is commented out, indicating a design decision:
- Commented:
chown -R sshuser:sshuser /samba-share(ownership change) - Active:
chmod -R 777 /samba-share(permission change)
graph LR
FileOps["File Operations"]
subgraph "Without 777"
SMBAsRoot["smbd (root)"]
SMBForce["force user: sshuser"]
FUSEOwner["FUSE mount owner: sshuser"]
NoAccess["Permission denied"]
end
subgraph "With 777"
SMBAsRoot2["smbd (root)"]
SMBForce2["force user: sshuser"]
FUSEOwner2["FUSE mount owner: sshuser"]
WriteAccess["Write successful"]
end
FileOps --> SMBAsRoot
SMBAsRoot --> SMBForce
SMBForce --> FUSEOwner
FUSEOwner --> NoAccess
FileOps --> SMBAsRoot2
SMBAsRoot2 --> SMBForce2
SMBForce2 --> FUSEOwner2
FUSEOwner2 --> WriteAccess
Permission Strategy
The 777 permission (read/write/execute for all users) is chosen over specific ownership for a critical reason:
The 777 permissions ensure that regardless of the complex UID/GID mapping between smbd (running as root but forced to sshuser) and the SSHFS mount, write operations succeed. This is a pragmatic trade-off favoring functionality over strict permissions within the isolated container environment.
Sources: Dockerfile:20-21
FUSE Configuration
Dockerfile24 modifies the FUSE configuration to enable cross-user filesystem access. This appends the user_allow_other directive to /etc/fuse.conf.
Why user_allow_other is Critical
By default, FUSE mounts are only accessible to the user who created the mount. Without user_allow_other:
sshuserruns:sshfs user@host:path /remote- Mount succeeds, files owned by
sshuser smbd(running as root) tries to access/samba-share/remote- Access denied - different user
With user_allow_other enabled, SSHFS mounts can be accessed by any user, including the smbd process. The allow_other option must also be passed to the sshfs command at runtime (see SSHFS Mount Options).
This is the critical enabler for Samba to serve SSHFS-mounted files.
Sources: Dockerfile24
Port Exposure Declaration
Dockerfile27 declares the ports used by the SMB protocol:
| Port | Protocol | Purpose |
|---|---|---|
| 139 | NetBIOS Session Service | Legacy SMB over NetBIOS |
| 445 | SMB over TCP | Direct SMB (modern) |
The EXPOSE directive is documentation only - it does not actually publish ports. Actual port forwarding is configured at container runtime via docker run -p 127.0.0.1:139:139 -p 127.0.0.1:445:445. See Running the Container for runtime port mapping.
Sources: Dockerfile27
Symbolic Link Creation
Dockerfile30 creates the critical integration point between SSHFS and Samba:
- Source:
/remote(SSHFS mount point) - Link:
/samba-share/remote(symlink visible to Samba)
Symbolic Link Integration
This symlink is created at build time, before any SSHFS mount exists. When the SSHFS mount occurs at runtime, the symlink automatically provides access to the mounted files through the Samba share.
This is a zero-copy integration - no data is duplicated. Reads and writes pass directly through the symlink to the FUSE mount.
Sources: Dockerfile30
Container Startup Command
Dockerfile33 defines the container's default startup command:
| Flag | Purpose |
|---|---|
--foreground | Run smbd in foreground instead of daemonizing |
--no-process-group | Prevent process group creation |
--debug-stdout | Send log output to stdout |
Why Foreground Mode
Docker containers require a foreground process as PID 1. When the foreground process exits, the container stops. Running smbd --foreground ensures:
- Container remains running while
smbdis active - Docker can monitor the process health
- Logs are visible via
docker logs - Graceful shutdown when container stops
The --no-process-group flag prevents smbd from creating additional process groups, simplifying container lifecycle management. The --debug-stdout flag routes log output to stdout, making it accessible via Docker's logging subsystem.
Sources: Dockerfile33
graph TB
subgraph "Build Context"
DockerfileSrc["Dockerfile"]
SmbConfSrc["smb.conf"]
end
subgraph "Base Layer"
UbuntuImage["ubuntu:latest"]
end
subgraph "Package Layer"
SSHFSPkg["sshfs package"]
SambaPkg["samba package"]
FuseLib["FUSE libs"]
SSHClient["openssh-client"]
SMBDBinary["smbd binary"]
end
subgraph "User Layer"
SSHUser["sshuser:1000"]
SSHPass["password: sshpass"]
HomeDir["/home/sshuser"]
end
subgraph "Filesystem Layer"
RemoteDir["/remote"]
SambaShareDir["/samba-share"]
SymbolicLink["/samba-share/remote -> /remote"]
end
subgraph "Configuration Layer"
SmbConfFile["/etc/samba/smb.conf"]
FuseConfFile["/etc/fuse.conf"]
PermSambaShare["chmod 777 /samba-share"]
end
subgraph "Runtime Layer"
SMBDProcess["smbd --foreground"]
Port139["Port 139"]
Port445["Port 445"]
end
DockerfileSrc --> UbuntuImage
UbuntuImage --> SSHFSPkg
UbuntuImage --> SambaPkg
SSHFSPkg --> FuseLib
SSHFSPkg --> SSHClient
SambaPkg --> SMBDBinary
SSHFSPkg --> SSHUser
SambaPkg --> SSHUser
SSHUser --> SSHPass
SSHUser --> HomeDir
SSHUser --> RemoteDir
SSHUser --> SambaShareDir
RemoteDir --> SymbolicLink
SambaShareDir --> SymbolicLink
SmbConfSrc --> SmbConfFile
SSHUser --> FuseConfFile
SambaShareDir --> PermSambaShare
SmbConfFile --> SMBDProcess
PermSambaShare --> SMBDProcess
SMBDBinary --> SMBDProcess
SMBDProcess --> Port139
SMBDProcess --> Port445
Complete Build Dependency Graph
This diagram maps Dockerfile components to their code entities and runtime dependencies:
This graph shows how each Dockerfile instruction contributes to the final container image, from base packages through configuration to the running smbd process.
Sources: Dockerfile:2-33
Build Layer Size Implications
Each RUN command creates a new image layer. The Dockerfile uses several optimization patterns:
- Combined Package Installation: Dockerfile:5-6 installs both
sshfsandsambain a singleRUNto minimize layers - Separate Directory Creation: Dockerfile12 and Dockerfile15 create directories in separate commands (could be optimized)
- Single Configuration Writes: Dockerfile24 appends to
fuse.confin one operation
The largest layers are:
- Package installation (100-200 MB including dependencies)
- Base Ubuntu image (~70 MB)
- Configuration and directory operations (<1 MB each)
Sources: Dockerfile:5-6 Dockerfile12 Dockerfile15 Dockerfile24
Summary
The Dockerfile constructs a complete SSHFS/Samba bridge server through 10 distinct build stages:
- Ubuntu base image selection
- Package installation (SSHFS + Samba)
- User account creation (
sshuser) - Directory structure (
/remote,/samba-share) - Samba configuration deployment
- Permission configuration (
777on/samba-share) - FUSE configuration (
user_allow_other) - Port exposure declaration (139, 445)
- Symbolic link creation (integration point)
- Startup command specification (
smbd --foreground)
The resulting image contains all necessary components for protocol translation between SSH and SMB, with the symbolic link at /samba-share/remote serving as the critical zero-copy integration mechanism.
Sources: Dockerfile:1-34