Containerized Netatalk
Netatalk comes with a Dockerfile and entry point script for running a containerized AFP server and AppleTalk services.
Out of the box, exactly two users, and two shared volumes are supported in this container. One of the shared volumes is a backup volume by default. If you need a different setup, please use the manual configuration option.
We use podman in these examples, but the Netatalk container should work with any OCI compliant container runtime such as Docker or CRI-O.
Make sure you have a container runtime installed, then build the netatalk container:
podman build -f distrib/docker/netatalk.Dockerfile .
Alternatively, pull a pre-built container image from Docker Hub or GHCR.
How to Run
Once you have the netatalk image on your machine run it with your preferred container runtime.
The easiest way to get started is to use the included compose.yml file.
podman compose -f distrib/docker/compose.yml up
You just need to edit the file to set the AFP_USER and AFP_PASS environment variables first.
Configuration
The easiest way to enable full functionality including Zeroconf service discovery and AppleTalk
is to use the host network driver.
For a hardened deployment, use the bridge network driver and expose port 548 for AFP.
However, Zeroconf service discovery may not work and you will have to manually specify
the IP address in the client when connecting to the file server.
It is recommended to set up a managed volume for persistent storage. Without this, the shared volume be stored in volatile storage that is lost upon container shutdown.
Below follows a sample podman run command for illustration.
Substitute AFP_USER and AFP_PASS with the appropriate user and password,
and ATALKD_INTERFACE with the network interface to use for AppleTalk.
We recommend named volumes for the shared volumes, but you can also use bind mounts to host machine directories if you prefer, as long as the permissions are set correctly for the AFP_USER and AFP_GROUP and AFP_UID, and AFP_GID if set to match the user and group ids on the host machine.
You also need to set the timezone with TZ to the IANA time zone ID
for your location, in order to get the correct time synchronized with the Timelord time server.
podman run --rm \
--network host \
--cap-add=NET_ADMIN \
--volume "afpshare:/mnt/afpshare" \
--volume "afpbackup:/mnt/afpbackup" \
--volume "/var/run/dbus:/var/run/dbus" \
--env AFP_USER= \
--env AFP_PASS= \
--env AFP_GROUP=afpusers \
--env ATALKD_INTERFACE=eth0 \
--env TZ=Europe/Stockholm \
--name netatalk netatalk:latest
Constraints
The most straight forward way to enable Zeroconf service discovery as well as the AppleTalk transport layer,
is to use the host network driver and NET_ADMIN capabilities.
Additionally, we rely on the host’s D-Bus for Zeroconf, achieved with a bind mount such as /var/run/dbus:/var/run/dbus.
The left hand side of the bind mount is the host machine, and the right hand side is the container.
The host machine path may have to be changed to match the location of D-Bus on the host machine.
On certain host OSes, notably Ubuntu: if the Apparmor security policy restricts D-Bus messages,
enable the unconfined security option.
Example for the docker compose yaml configuration file:
security_opt:
- apparmor=unconfined
See the Docker AppArmor security profiles documentation for further details.
The container is hard coded to output afpd (the Netatalk file server daemon) logs to the container’s stdout,
with default log level info. Logs from the AppleTalk daemons are sent to the syslog.
File search on mounted volumes in the Finder is supported with the cnid Spotlight backend.
The cnid backend supports filename search only, no file content or metadata index searching.
This is recommend over localsearch/xapian (full content indexing) in production environments
for technical and privacy reasons.
MySQL CNID Backend
The MySQL CNID backend is an alternative to the default Berkeley DB CNID backend which offers better scalability.
Here follows a compose example to run a container network with MariaDB as the database for the MySQL CNID backend, plus a web interface to administer the database for good measure.
Set AFP_CNID_SQL_PASS and MARIADB_ROOT_PASSWORD to the same password.
services:
netatalk:
image: netatalk:latest
networks:
- afp_network
ports:
- "548:548"
volumes:
- afpshare:/mnt/afpshare
- afpbackup:/mnt/afpbackup
- /var/run/dbus:/var/run/dbus
environment:
- AFP_USER=atalk1
- AFP_USER2=atalk2
- AFP_PASS=
- AFP_PASS2=
- AFP_GROUP=afpusers
- AFP_CNID_BACKEND=mysql
- AFP_CNID_SQL_HOST=cnid_db
- AFP_CNID_SQL_PASS=
depends_on:
- cnid_db
cnid_db:
image: mariadb:latest
restart: always
networks:
- afp_network
volumes:
- cnid_db_data:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=
adminer:
image: adminer:latest
restart: always
networks:
- afp_network
ports:
- 8081:8080
volumes:
afpshare:
name: afpshare
afpbackup:
name: afpbackup
cnid_db_data:
name: cnid_db_data
networks:
afp_network:
driver: bridge
Webmin Module
Netatalk’s Webmin module is distributed as a separate container image. The module allows you to administer the Netatalk configuration via a web interface. Here follows an example compose configuration to run the module in a container.
Note that since the netatalk daemons run in a separate container, we cannot control
the services directly. Instead, activate polling of changes to the afp.conf
configuration file. Set AFP_CONFIG_POLLING to the number of seconds to wait between
polling attempts.
services:
netatalk:
image: netatalk:latest
networks:
- afp_network
ports:
- "548:548"
volumes:
- afpshare:/mnt/afpshare
- afpbackup:/mnt/afpbackup
- afpconf:/etc/netatalk
- /var/run/dbus:/var/run/dbus
environment:
- AFP_USER=atalk1
- AFP_USER2=atalk2
- AFP_PASS=
- AFP_PASS2=
- AFP_GROUP=afpusers
- AFP_CONFIG_POLLING=5
- MANUAL_CONFIG=1
webmin:
image: netatalk_webmin_module:latest
networks:
- afp_network
ports:
- "10000:10000"
volumes:
- afpconf:/etc/netatalk
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WEBMIN_USER=admin
- WEBMIN_PASS=
depends_on:
- netatalk
volumes:
afpshare:
name: afpshare
afpbackup:
name: afpbackup
afpconf:
name: afpconf
networks:
afp_network:
driver: bridge
Printing
The CUPS administrative web app is running on port 631 in the container,
which is exposed to the host machine by default when using the host network driver.
This is used to configure CUPS compatible printers for printing from an old Mac or Apple IIGS.
You may have to restart papd (or the entire container) after adding a CUPS printer for it to be picked up as an AppleTalk printer.
Environment Variables
Mandatory Settings
These are required to set the credentials used to authenticate with the file server.
| Variable | Description |
|---|---|
| AFP_USER | Primary user of the shared volumes |
| AFP_PASS | Password to authenticate with the primary user (8 chars or less when using INSECURE_AUTH) |
| AFP_GROUP | Group that owns the shared volume dirs |
Mandatory for AppleTalk
| Variable | Description |
|---|---|
| ATALKD_INTERFACE | The network interface to use for AppleTalk |
Optional Settings
Value Type
Set this environment variable to a specific value or string.
| Variable | Description |
|---|---|
| User & Group Configuration | |
| AFP_UID | Specify user id of AFP_USER |
| AFP_GID | Specify group id of AFP_GROUP |
| AFP_USER2 | Username for the secondary user |
| AFP_PASS2 | Password for the secondary user |
| Server Configuration | |
| SERVER_NAME | The name of the server (AFP and Zeroconf) |
| SHARE_NAME | The name of the primary shared volume |
| SHARE_NAME2 | The name of the secondary shared (Time Machine) volume |
| AFP_LOGLEVEL | The verbosity of logs; default is “info” |
| AFP_MIMIC_MODEL | Use a custom macOS (OSX) AFP icon; examples: Tower, RackMount |
| AFP_LEGACY_ICON | Use a custom Classic Mac OS AFP icon; examples: daemon, sdcard |
| AFP_LOGIN_MESSAGE | A message to display when a user logs in (Classic Mac OS only) |
| ATALKD_OPTIONS | A string with options to append to atalkd.conf |
| Authentication Configuration | |
| AFP_UAMS | Space-separated list of UAM .so files to load, overriding the default list (uams_dhx2.so uams_srp.so) as well as the INSECURE_AUTH flag |
| CNID Database Configuration | |
| AFP_CNID_BACKEND | The backend to use for the CNID database: dbd, sqlite, or mysql |
| AFP_CNID_SQL_HOST | The hostname or IP address of the CNID SQL server |
| AFP_CNID_SQL_USER | The username to use when connecting to the CNID SQL server |
| AFP_CNID_SQL_PASS | The password to use when connecting to the CNID SQL server |
| AFP_CNID_SQL_DB | The name of the designated database in the SQL server |
| Directory Cache Configuration | |
| AFP_DIRCACHESIZE | Directory cache size in entries (default: 65536) |
| AFP_DIRCACHE_MODE | Cache algorithm: lru (default) or arc |
| AFP_DIRCACHE_VALIDATION_FREQ | Validate cache every Nth access (default: 1, higher = better performance) |
| AFP_DIRCACHE_RFORK_BUDGET | Total memory budget in KB for resource fork caching (default: 0 = disabled) |
| AFP_DIRCACHE_RFORK_MAXSIZE | Max size in KB of a single cached resource fork entry (default: 1024) |
| Charset Configuration | |
| AFP_MAC_CHARSET | Mac client charset (default: MAC_ROMAN); see afp.conf man page |
| AFP_UNIX_CHARSET | Server filesystem charset (default: UTF8); see afp.conf man page |
| AFP_VOL_CHARSET | Volume charset (default: UTF8); see afp.conf man page |
| Test Suite Configuration | |
| TESTSUITE | Run test suite on startup: spectest, lan, speed, login, readonly |
| AFP_VERSION | AFP protocol version for tests: 1-7 (default: 7 = AFP 3.4) |
| AFP_HOST | AFP server hostname/IP for tests (default: 127.0.0.1) |
| AFP_PORT | AFP server port for tests (default: 548) |
| AFP_REMOTE | Test remote AFP server (vs container-local) |
| TEST_FLAGS | Additional flags to pass to test binaries |
| Other Configuration | |
| TZ | The timezone to use in the container |
| AFP_CONFIG_POLLING | Poll afp.conf for changes every N seconds (for Webmin integration) |
| AFP_CONVERT_APPLEDOUBLE | Convert AppleDouble files on the volume: yes or no (default) |
Boolean Type
Set these environment variables to a non-zero value to enable, ex. “1”
| Variable | Description |
|---|---|
| Volume & Access Configuration | |
| AFP_DROPBOX | Enable dropbox mode; secondary user is guest with read only access to the second shared volume |
| AFP_READONLY | Mount volume as read-only |
| DISABLE_TIMEMACHINE | The secondary shared volume is a regular volume, not a backup volume |
| File System Configuration | |
| AFP_EXTMAP | Enable mapping of filename extension to Classic Mac OS type/creator |
| AFP_ADOUBLE | Use AppleDouble (ad) instead of system native EA for extended attributes |
| Authentication Configuration | |
| INSECURE_AUTH | Enable the “RandNum”, “ClearTxt”, and “Guest” UAMs; the AFP password must be 8 chars or shorter; use AFP_UAMS for finer control |
| Advanced Configuration | |
| MANUAL_CONFIG | Enable manual management of configurations; overrides most other options |
| VERBOSE | Enable verbose test output |
| SERVER_LOGS | Display afpd server logs after test completion |
| IO_MONITORING | Enable I/O monitoring for lantest (requires --privileged) |
| DEBUG_ENTRY_SCRIPT | Enable bash debug mode (set -x) for entrypoint script |