Dev Docs Netatalk Configuration
- Netatalk Configuration System
- 1. Configuration Overview
- 2. Configuration File Format
- 3. Configuration Architecture
- 4. INI Parser Integration
- 5. Configuration Loading Flow
- 6. Variable Substitution
- 7. Global Section Options
- 8. Cache Configuration Options
- 9. Volume Section Options
- 10. Homes Section
- 11. Network Configuration
- 12. Authentication Configuration
- 13. Logging Configuration
- 14. FCE (File Change Event) Configuration
- 15. struct afp_options Key Fields
- 16. Command-Line Options
- 17. Complete Configuration Example
- 18. Configuration Reload
Netatalk Configuration System
1. Configuration Overview
Netatalk uses a centralized INI-style configuration file (afp.conf) to control all aspects of the AFP file server: global daemon settings, authentication, network binding, logging, cache tuning, charset handling, and per-volume options. The configuration system is built around the third-party iniparser library, wrapped with Netatalk-specific helpers that implement section inheritance, variable substitution, and type-safe option extraction.
Key Source Files
| Component | File |
|---|---|
| Default config template | config/afp.conf.in |
| Main config parsing | libatalk/util/netatalk_conf.c |
| Command-line options | etc/afpd/afp_options.c |
| DSI/network initialization | etc/afpd/afp_config.c |
| Global structures & flags | include/atalk/globals.h |
| Public config API | include/atalk/netatalk_conf.h |
| Volume structures & flags | include/atalk/volume.h |
| INI parser compatibility | include/atalk/iniparser_util.h |
| Directory cache header | etc/afpd/dircache.h |
2. Configuration File Format
The configuration file uses standard INI format with three section types:
; Comments use semicolons or hash marks
# This is also a comment
[Global]
; Server-wide settings — parsed by afp_config_parse()
hostname = My Server
log level = default:note
[Homes]
; Special section for auto-generated user home volumes
basedir regex = /home
[MyVolume]
; Named volume sections
path = /srv/share
Section constants defined in include/atalk/iniparser_util.h:
| Constant | Value | Purpose |
|---|---|---|
INISEC_GLOBAL |
"global" |
[Global] section name (case-insensitive) |
INISEC_HOMES |
"homes" |
[Homes] section name (case-insensitive) |
| Any other name | — | A volume definition |
Template installed at build time from config/afp.conf.in:
[Global]
; Global server settings
[Homes]
basedir regex = @homedir@
; [my volume]
; path = /path/to/volume
; volume name = My AFP Volume
3. Configuration Architecture
%%{ init: { 'themeVariables': { 'fontSize': '14px' }, 'flowchart': { 'nodeSpacing': 15, 'rankSpacing': 40 } } }%%
graph TD
A["afp.conf<br/>INI config file"]:::orange
B["dictionary *config<br/>iniparser in-memory dict"]:::purple
C["afp_config_parse()<br/>netatalk_conf.c<br/>Parse [Global] options"]:::blue
D["struct afp_options<br/>globals.h<br/>All global settings"]:::green
E["configinit()<br/>afp_config.c<br/>DSI/network + FCE setup"]:::cyan
F["load_volumes()<br/>netatalk_conf.c<br/>Parse volume sections"]:::blue
G["readvolfile()<br/>netatalk_conf.c<br/>Iterate INI sections"]:::purple
H["creatvol()<br/>netatalk_conf.c<br/>Create struct vol"]:::purple
I["struct vol<br/>volume.h<br/>Per-volume settings"]:::green
A -->|"iniparser_load()"| B
B --> C
C --> D
D --> E
B --> F
F --> G
G --> H
H --> I
classDef blue fill:#74b9ff,stroke:#333,rx:10,ry:10
classDef purple fill:#a29bfe,stroke:#333,rx:10,ry:10
classDef green fill:#55efc4,stroke:#333,rx:10,ry:10
classDef yellow fill:#ffeaa7,stroke:#333,rx:10,ry:10
classDef orange fill:#ff9f43,stroke:#333,rx:10,ry:10
classDef cyan fill:#81ecec,stroke:#333,rx:10,ry:10
classDef salmon fill:#fab1a0,stroke:#333,rx:10,ry:10
4. INI Parser Integration
Netatalk uses the external iniparser library (included as a Meson subproject dependency). The integration layer in include/atalk/iniparser_util.h provides:
Compatibility Layer
Handles API differences between iniparser versions:
// Conditional include for system vs bundled iniparser
#ifdef HAVE_INIPARSER_INIPARSER_H
#include <iniparser/iniparser.h>
#else
#include <iniparser.h>
#endif
// Handle const-correctness differences between iniparser versions
#ifdef HAVE_INIPARSER_CONST_DICTIONARY
#define INIPARSER_DICTIONARY const dictionary
#else
#define INIPARSER_DICTIONARY dictionary
#endif
Helper Macros
| Macro | Purpose |
|---|---|
INIPARSER_GETSTR() |
Get string from section:key — returns pointer into dict |
INIPARSER_GETSTRDUP() |
Get heap-duplicated string from section:key |
CONFIG_ARG_FREE() |
Free and NULL a config string pointer |
Option Retrieval Functions
Internal to netatalk_conf.c, four typed helpers implement section fallback (volume → preset → default):
| Function | Return Type | Description |
|---|---|---|
getoption_str() |
const char * |
Returns pointer into iniparser dict |
getoption_strdup() |
char * |
Returns heap-allocated copy |
getoption_bool() |
int |
Boolean via iniparser_getboolean() |
getoption_int() |
int |
Integer via iniparser_getint() |
Each follows the pattern: try vol:opt, then defsec:opt (preset section), then return defval.
The vdgoption_bool() variant adds a third fallback to [Global] before returning the default — used for options that can be set globally and overridden per-volume (e.g., force xattr with sticky bit).
5. Configuration Loading Flow
Phase 1: afp_config_parse()
Called once at startup from the master afpd process. Defined in netatalk_conf.c function afp_config_parse().
%%{ init: { 'themeVariables': { 'fontSize': '14px' }, 'flowchart': { 'nodeSpacing': 15, 'rankSpacing': 40 } } }%%
flowchart TD
A["afp_config_parse(obj, processname)"]:::yellow
B["Set defaults:<br/>configfile, sigconffile,<br/>uuidconf, flags"]:::cream
C["iniparser_load(configfile)<br/>as root"]:::orange
D["Parse logging early:<br/>log level, log file"]:::green
E["setuplog() — init logging"]:::green
F["Parse [Global] boolean flags<br/>→ options->flags bitmask"]:::blue
G["Parse [Global] string options<br/>hostname, uamlist, etc."]:::blue
H["Parse [Global] integer options<br/>connections, tickleval, etc."]:::blue
I["Parse cache configuration<br/>dircache size/mode/rfork"]:::purple
J["Parse charset options<br/>unix/vol/mac charset"]:::cyan
K["readextmap() —<br/>type/creator mappings"]:::cyan
L["Validate & clamp values"]:::salmon
A --> B --> C --> D --> E --> F --> G --> H --> I --> J --> K --> L
classDef blue fill:#74b9ff,stroke:#333,rx:10,ry:10
classDef purple fill:#a29bfe,stroke:#333,rx:10,ry:10
classDef green fill:#55efc4,stroke:#333,rx:10,ry:10
classDef yellow fill:#ffeaa7,stroke:#333,rx:10,ry:10
classDef cream fill:#f8f4e8,stroke:#333,rx:10,ry:10
classDef orange fill:#ff9f43,stroke:#333,rx:10,ry:10
classDef cyan fill:#81ecec,stroke:#333,rx:10,ry:10
classDef salmon fill:#fab1a0,stroke:#333,rx:10,ry:10
Key steps in order:
- Set config file path from command line or default
_PATH_CONFDIR "afp.conf" - Load INI file with
iniparser_load()as root - Parse logging first for early error reporting
- Parse all
[Global]boolean flags intooptions->flagsbitmask - Parse string options (hostname, UAM list, charsets, etc.)
- Parse integer options (connections, timeout, buffer sizes, etc.)
- Parse cache configuration (dircache size, mode, rfork budget)
- Parse charset options (unix, vol, mac charset)
- Load extension mappings via
readextmap() - Validate and clamp values (tickleval ≥ 1, dsireadbuf ≥ 6, volnamelen 8–255)
Phase 2: load_volumes()
Called from both the master (for Zeroconf announcements) and forked children (for actual volume serving). Defined in netatalk_conf.c function load_volumes().
%%{ init: { 'themeVariables': { 'fontSize': '14px' }, 'flowchart': { 'nodeSpacing': 15, 'rankSpacing': 40 } } }%%
flowchart TD
A["load_volumes(obj, flags)"]:::yellow
B{"Config file<br/>changed?"}:::cream
C["Re-read INI file<br/>iniparser_load()"]:::orange
D["readvolfile(obj, pwent)<br/>Iterate INI sections"]:::blue
E{"Section ==<br/>[Homes]?"}:::cream
F["Match basedir regex<br/>Build home path<br/>Substitute $u in name"]:::purple
G["Read path, volume name,<br/>vol preset from section"]:::blue
H["creatvol(obj, pwd,<br/>section, name, path, preset)"]:::green
I["Prune deleted volumes<br/>from linked list"]:::salmon
J["Rewrite afp_voluuid.conf"]:::cyan
A --> B
B -->|"No (skip)"| DONE["Return"]:::cream
B -->|"Yes"| C --> D
D --> E
E -->|"Yes"| F --> H
E -->|"No"| G --> H
H --> I --> J
classDef blue fill:#74b9ff,stroke:#333,rx:10,ry:10
classDef purple fill:#a29bfe,stroke:#333,rx:10,ry:10
classDef green fill:#55efc4,stroke:#333,rx:10,ry:10
classDef yellow fill:#ffeaa7,stroke:#333,rx:10,ry:10
classDef cream fill:#f8f4e8,stroke:#333,rx:10,ry:10
classDef orange fill:#ff9f43,stroke:#333,rx:10,ry:10
classDef cyan fill:#81ecec,stroke:#333,rx:10,ry:10
classDef salmon fill:#fab1a0,stroke:#333,rx:10,ry:10
Load flags (from include/atalk/volume.h lv_flags_t):
| Flag | Value | Purpose |
|---|---|---|
LV_DEFAULT |
0 |
Load in user/session context (honors authorization) |
LV_ALL |
1 |
Skip access checks (master process) |
LV_FORCE |
2 |
Reload even if config file timestamp unchanged |
Phase 3: creatvol()
Defined in netatalk_conf.c function creatvol(), creates a struct vol from INI options:
- Check if volume already exists (reloading: clear
v_deletedflag and return) - Check access lists (
valid users,invalid users,hosts allow,hosts deny) - Parse per-volume options: password, veto files, charsets, paths, permissions
- Parse EA mode, casefold settings, boolean volume flags into
v_flagsbitmask - Handle charset conversion for volume name (UTF-8-MAC and Mac charset)
- Assign volume ID, check EA support, initialize VFS
- Get or generate UUID for Time Machine Zeroconf record
- Insert into global
Volumeslinked list
Option Precedence
For volume options, the resolution order is:
Volume Section → vol preset Section → [Global] (for vdg options) → Default Value
The vol preset mechanism allows shared defaults:
[Global]
vol preset = my_defaults
[my_defaults]
ea = sys
unix priv = yes
[Volume1]
path = /data1
; Inherits ea=sys and unix priv=yes from my_defaults
6. Variable Substitution
The volxlate() function in netatalk_conf.c expands variables in volume paths and names:
| Variable | Expansion | Context Required |
|---|---|---|
$b |
Basename of path | Path available |
$c |
Client IP:port or AppleTalk address | AFP session |
$d |
Volume path on server | Path available |
$f |
User full name (GECOS field) | Login context |
$g |
User primary group name | Login context |
$h |
Server hostname | Always |
$i |
Client IP (no port) | AFP session |
$s |
Server name (or hostname) | Always |
$u |
Username | Login context |
$v |
Volume name (or path basename) | Path/volname |
$$ |
Literal $ |
Always |
7. Global Section Options
All options parsed in afp_config_parse() in netatalk_conf.c and stored in struct afp_options defined in globals.h.
Boolean Flag Options
Each sets a bit in options->flags. Flag constants defined in globals.h.
| Config Key | Flag Constant | Default | Description |
|---|---|---|---|
zeroconf |
OPTION_NOZEROCONF |
true |
Enable Bonjour/mDNS advertisement (flag set when false) |
advertise ssh |
OPTION_ANNOUNCESSH |
false |
Advertise SSH capability in Zeroconf |
close vol |
OPTION_CLOSEVOL |
false |
Disconnect users when volume removed from config |
client polling |
OPTION_SERVERNOTIF |
false |
Use client polling instead of server notifications (flag set when false) |
use sendfile |
OPTION_NOSENDFILE |
true |
Use sendfile() for file transfers (flag set when false) |
recvfile |
OPTION_RECVFILE |
false |
Use splice/recvfile for writes |
solaris share reservations |
OPTION_SHARE_RESERV |
true |
Use Solaris fcntl F_SHARE locks |
afpstats |
OPTION_DBUS_AFPSTATS |
false |
Run D-Bus thread for AFP statistics |
afp read locks |
OPTION_AFP_READ_LOCK |
false |
AFP spec conforming read locks |
spotlight |
OPTION_SPOTLIGHT_VOL |
false |
Enable Spotlight by default for volumes |
spotlight expr |
OPTION_SPOTLIGHT_EXPR |
true |
Allow Spotlight logic expressions |
veto message |
OPTION_VETOMSG |
false |
Send AFP message for veto file access |
valid shellcheck |
OPTION_VALID_SHELLCHECK |
true |
Check for valid login shell |
appletalk |
OPTION_DDP |
false |
Enable AppleTalk/DDP (compile-time: NO_DDP) |
Password Options
Stored in options->passwdbits:
| Config Key | Flag | Default | Description |
|---|---|---|---|
save password |
PASSWD_NOSAVE |
true |
Allow clients to save passwords (flag set when false) |
set password |
PASSWD_SET |
false |
Allow users to change passwords |
String Options
| Config Key | Field in struct afp_options |
Default |
|---|---|---|
log level |
logconfig |
"default:note" |
log file |
logfile |
NULL (syslog) |
login message |
loginmesg |
NULL |
guest account |
guest |
"nobody" |
extmap file |
extmapfile |
_PATH_CONFDIR "extmap.conf" |
passwd file |
passwdfile |
_PATH_AFPDPWFILE |
uam path |
uampath |
_PATH_AFPDUAMPATH |
uam list |
uamlist |
"uams_dhx.so uams_dhx2.so" |
afp port |
port |
"548" |
signature |
signatureopt |
"" |
k5 service |
k5service |
NULL |
k5 realm |
k5realm |
NULL |
k5 keytab |
k5keytab |
NULL |
afp listen |
listen |
NULL |
afp interfaces |
interfaces |
NULL |
hostname |
hostname |
System hostname via gethostname() |
server name |
servername |
NULL (uses hostname); deprecated alias: zeroconf name |
mimic model |
mimicmodel |
NULL |
legacy icon |
legacyicon |
"" |
nt domain |
ntdomain |
NULL |
ad domain |
addomain |
NULL |
nt separator |
ntseparator |
NULL |
admin auth user |
adminauthuser |
NULL |
ignored attributes |
ignored_attr |
NULL |
fqdn |
fqdn |
NULL |
map acls |
flags | "rights" (sets OPTION_ACL2MACCESS) |
cnid server |
Cnid_srv + Cnid_port |
"localhost:4700" |
cnid mysql host |
cnid_mysql_host |
NULL |
cnid mysql user |
cnid_mysql_user |
NULL |
cnid mysql pw |
cnid_mysql_pw |
NULL |
cnid mysql db |
cnid_mysql_db |
NULL |
ddp address |
ddpaddr |
"0.0" |
ddp zone |
zone |
NULL |
Integer Options
| Config Key | Field | Default | Constraints |
|---|---|---|---|
max connections |
connections |
200 |
— |
passwd minlen |
passwdminlen |
0 |
— |
tickleval |
tickleval |
30 |
Clamped to ≥ 1 |
timeout |
timeout |
4 |
Clamped to ≥ 1 |
dsireadbuf |
dsireadbuf |
12 |
Clamped to ≥ 6 |
server quantum |
server_quantum |
DSI_SERVQUANT_DEF |
— |
volnamelen |
volnamelen |
80 |
Clamped to 8–255 |
tcpsndbuf |
tcp_sndbuf |
0 (OS default) |
— |
tcprcvbuf |
tcp_rcvbuf |
0 (OS default) |
— |
sleep time |
sleep |
10 (hours → tickles) |
Min 4 tickles |
disconnect time |
disconnected |
24 (hours → tickles) |
— |
splice size |
splice_size |
65536 (64 KB) |
— |
sparql results limit |
sparql_limit |
0 (unlimited) |
— |
fce holdfmod |
fce_fmodwait |
60 (seconds) |
— |
fce sendwait |
fce_sendwait |
0 (ms) |
— |
Misc Options
| Config Key | Field | Default | Description |
|---|---|---|---|
log microseconds |
log_us_timestamp |
true |
Include μs timestamps in log |
admin group |
admingid |
0 |
Group name resolved to GID |
force user |
force_uid |
not set | Force all operations as this user |
force group |
force_gid |
not set | Force all operations as this group |
vol dbnest |
(boolean, used in creatvol()) |
false |
Store CNID database inside the volume directory instead of vol dbpath |
Charset Options
| Config Key | Field | Default | Scope |
|---|---|---|---|
unix charset |
unixcodepage |
"UTF8" |
Global only |
vol charset |
volcodepage |
Same as unix charset | Global + Volume |
mac charset |
maccodepage |
"MAC_ROMAN" |
Global + Volume |
8. Cache Configuration Options
All cache options are [Global] section only, parsed in afp_config_parse().
Directory Cache
| Config Key | Field | Default | Description |
|---|---|---|---|
dircache size |
dircachesize |
8192 (DEFAULT_MAX_DIRCACHE_SIZE) |
Max directory cache entries |
dircachesize |
(deprecated alias) | — | Legacy name; prints deprecation warning |
dircache mode |
dircache_mode |
0 (LRU) |
Cache eviction policy: "lru" or "arc" |
dircache validation freq |
dircache_validation_freq |
1 (DEFAULT_DIRCACHE_VALIDATION_FREQ) |
Validate every Nth access (1 = every access) |
Runtime bounds enforced in etc/afpd/dircache.h:
| Constant | Value | Description |
|---|---|---|
MIN_DIRCACHE_SIZE |
1024 |
Minimum entries (testing/constrained) |
DEFAULT_DIRCACHE_SIZE |
65536 |
Production default (in dircache.h) |
MAX_DIRCACHE_SIZE |
1048576 |
Maximum entries (1M) |
Note:
DEFAULT_MAX_DIRCACHE_SIZE(8192) inglobals.his the config-level default when no option is specified. Thedircache.hbounds are applied at runtime indircache_init().
Resource Fork Cache (Tier 2)
The resource fork data cache stores small rfork content directly in directory cache entries, avoiding repeated disk I/O for icon and metadata resource forks.
| Config Key | Field | Default | Hard Cap | Description |
|---|---|---|---|---|
dircache rfork budget |
dircache_rfork_budget |
0 (disabled) |
RFORK_BUDGET_MAX_KB = 10 GB |
Total memory budget for rfork cache (KB) |
dircache rfork maxsize |
dircache_rfork_maxentry |
1024 KB |
RFORK_ENTRY_MAX_KB = 10 MB |
Max rfork size per entry (KB) |
Hard cap constants from globals.h:
#define RFORK_BUDGET_MAX_KB (10 * 1024 * 1024) /* 10 GB in KB */
#define RFORK_ENTRY_MAX_KB (10 * 1024) /* 10 MB in KB */
On 32-bit platforms, the budget is further clamped to SIZE_MAX / 1024 to prevent overflow when converting KB to bytes.
Runtime globals exposed in etc/afpd/dircache.h:
| Variable | Type | Description |
|---|---|---|
rfork_cache_used |
size_t |
Current memory used by cached rfork data |
rfork_cache_budget |
size_t |
Configured budget in bytes |
rfork_max_entry_size |
size_t |
Max entry size in bytes |
rfork_lru_count |
unsigned int |
Entries in LRU eviction queue |
rfork_lru |
q_t * |
LRU eviction queue pointer |
Statistics counters (also in dircache.h):
| Counter | Description |
|---|---|
rfork_stat_lookups |
Total cache lookups |
rfork_stat_hits |
Cache hits |
rfork_stat_misses |
Cache misses |
rfork_stat_added |
Entries added |
rfork_stat_evicted |
Entries evicted (LRU) |
rfork_stat_invalidated |
Entries invalidated |
rfork_stat_used_max |
High water mark of used memory |
Cache Configuration Diagram
%%{ init: { 'themeVariables': { 'fontSize': '14px' }, 'flowchart': { 'nodeSpacing': 15, 'rankSpacing': 40 } } }%%
graph LR
subgraph "afp.conf [Global]"
A["dircache size = 8192"]:::orange
B["dircache mode = lru"]:::orange
C["dircache validation freq = 1"]:::orange
D["dircache rfork budget = 0"]:::orange
E["dircache rfork maxsize = 1024"]:::orange
end
F["struct afp_options"]:::green
A --> F
B --> F
C --> F
D --> F
E --> F
F --> G["dircache_init()"]:::blue
G --> H["Tier 1:<br/>Directory Cache<br/>Hash + LRU/ARC"]:::cyan
G --> I["Tier 2:<br/>Rfork Data Cache<br/>LRU within budget"]:::purple
classDef blue fill:#74b9ff,stroke:#333,rx:10,ry:10
classDef purple fill:#a29bfe,stroke:#333,rx:10,ry:10
classDef green fill:#55efc4,stroke:#333,rx:10,ry:10
classDef orange fill:#ff9f43,stroke:#333,rx:10,ry:10
classDef cyan fill:#81ecec,stroke:#333,rx:10,ry:10
9. Volume Section Options
All volume options are parsed in creatvol() in netatalk_conf.c. Many support preset fallback via vol preset.
Required / Path Options
| Config Key | Field | Default | Description |
|---|---|---|---|
path |
v_path |
required | Filesystem path to share |
volume name |
v_localname |
Section name | Display name of volume |
vol preset |
— | [Global] vol preset |
Inherit defaults from named section |
Access Control Options
| Config Key | Lookup Function | Description |
|---|---|---|
valid users |
accessvol() |
Comma/space-separated users/groups (@group) allowed |
invalid users |
accessvol() |
Users/groups denied access |
hosts allow |
hostaccessvol() |
IP/CIDR whitelist |
hosts deny |
hostaccessvol() |
IP/CIDR blacklist |
rolist |
accessvol() |
Users/groups forced read-only |
rwlist |
accessvol() |
Users/groups with read-write (others become read-only) |
String Volume Options
| Config Key | Field | Default |
|---|---|---|
password |
v_password |
NULL |
veto files |
v_veto |
NULL |
vol charset |
v_volcodepage |
Global vol charset |
mac charset |
v_maccodepage |
Global mac charset |
cnid scheme |
v_cnidscheme |
DEFAULT_CNID_SCHEME |
cnid server |
v_cnidserver:v_cnidport |
Global cnid server |
vol dbpath |
v_dbpath |
_PATH_STATEDIR "CNID/" |
legacy icon |
v_legacyicon |
Global legacy icon |
volume uuid |
v_uuid |
Auto-generated |
preexec |
v_preexec |
NULL |
postexec |
v_postexec |
NULL |
Numeric Volume Options
| Config Key | Field | Default | Format |
|---|---|---|---|
umask |
v_umask |
Global umask | Octal |
directory perm |
v_dperm |
0 |
Octal |
file perm |
v_fperm |
0 |
Octal |
vol size limit |
v_limitsize |
0 (unlimited) |
MiB |
EA Configuration
| Config Key | EA Mode | adouble Version | Description |
|---|---|---|---|
ea = sys |
AFPVOL_EA_SYS |
AD_VERSION_EA |
Native filesystem xattrs |
ea = samba |
AFPVOL_EA_SYS + AFPVOL_EA_SAMBA |
AD_VERSION_EA |
Samba-compatible (appends NULL byte) |
ea = ad |
AFPVOL_EA_AD |
AD_VERSION2 |
AppleDouble v2 files |
ea = none |
AFPVOL_EA_NONE |
— | No EA support |
| (default) | AFPVOL_EA_AUTO |
— | Try sys, fallback |
EA mode constants from volume.h:
| Constant | Value |
|---|---|
AFPVOL_EA_NONE |
0 |
AFPVOL_EA_AUTO |
1 |
AFPVOL_EA_SYS |
2 |
AFPVOL_EA_AD |
3 |
Casefold Options
| Config Key | Values | Default |
|---|---|---|
casefold |
tolower, toupper, xlatelower, xlateupper |
— |
case sensitive |
boolean | true |
Boolean Volume Flags
Each sets a bit in v_flags. Flag constants defined in volume.h.
| Config Key | Flag | Default | Description |
|---|---|---|---|
read only |
AFPVOL_RO |
false |
Volume is read-only |
invisible dots |
AFPVOL_INV_DOTS |
false |
Dot files are invisible |
legacy volume size |
AFPVOL_LIMITSIZE |
false |
Limit size for older Macs |
prodos |
AFPVOL_A2VOL |
false |
ProDOS volume |
stat vol |
AFPVOL_NOSTAT |
true |
Stat volume (flag set when false) |
unix priv |
AFPVOL_UNIX_PRIV |
true |
Support Unix privileges |
cnid dev |
AFPVOL_NODEV |
true |
Use device number in CNID (flag set when false) |
illegal seq |
AFPVOL_EILSEQ |
false |
Encode illegal sequences as-is |
time machine |
AFPVOL_TM |
false |
Time Machine support |
search db |
AFPVOL_SEARCHDB |
false |
Use CNID DB for searches |
network ids |
AFPVOL_NONETIDS |
true |
Send network IDs (flag set when false) |
acls |
AFPVOL_ACLS |
true |
Enable ACL support (compile-time: HAVE_ACLS) |
convert appledouble |
AFPVOL_NOV2TOEACONV |
false |
Convert adouble v2→EA (flag set when false) |
follow symlinks |
AFPVOL_FOLLOWSYM |
false |
Follow symlinks on server |
spotlight |
AFPVOL_SPOTLIGHT |
Global spotlight | Enable Spotlight indexing for volume |
delete veto files |
AFPVOL_DELVETO |
false |
Delete veto files on directory removal |
preexec close |
v_preexec_close |
false |
Close volume if preexec command fails |
force xattr with sticky bit |
AFPVOL_FORCE_STICKY_XATTR |
false |
Write metadata xattr as root on sticky dirs (uses vdgoption_bool — inherits from [Global]) |
Chmod Request Handling
| Value | Flag | Behavior |
|---|---|---|
preserve (default) |
AFPVOL_CHMOD_PRESERVE_ACL |
Preserve ACLs during chmod |
ignore |
AFPVOL_CHMOD_IGNORE |
Ignore chmod requests entirely |
simple |
(no flag) | Simple chmod passthrough |
Ignored Attributes
The ignored attributes option (volume or global) controls which AFP attributes are ignored:
| Value | Effect |
|---|---|
all |
Ignore nowrite, norename, nodelete |
nowrite |
Ignore write-protect attribute |
norename |
Ignore rename-protect attribute |
nodelete |
Ignore delete-protect attribute |
10. Homes Section
The [Homes] section auto-generates volumes for user home directories. Parsed in readvolfile() in netatalk_conf.c.
| Config Key | Default | Description |
|---|---|---|
basedir regex |
required | POSIX extended regex matching user home parent directories |
home name |
"$u's home" |
Volume name template (must contain $u) |
path |
NULL |
Subdirectory within home to share |
Matching logic:
- Get user’s
pw_dirfrom passwd database - Resolve to realpath
- Match against
basedir regex(POSIX extended regex viaregcomp()/regexec()) - If
pathis set, append it to the home directory - Create volume with name from
home name(with$usubstituted)
11. Network Configuration
Listening Addresses
Configured in [Global] and processed in configinit() in afp_config.c:
[Global]
; Bind to specific addresses (comma or space separated)
afp listen = 192.168.1.100:548, [::1]:548
; Bind to network interfaces (requires getifaddrs)
afp interfaces = eth0, eth1
; Default port
afp port = 548
Processing order in configinit():
- If
afp listenis set → create DSI listeners for each address - If
afp interfacesis set → resolve viagetifaddrs()and create DSI listeners - If neither produced a listener → create a wildcard DSI listener
TCP Tuning
| Config Key | Default | Description |
|---|---|---|
tcpsndbuf |
0 (OS default) |
TCP send buffer size in bytes |
tcprcvbuf |
0 (OS default) |
TCP receive buffer size in bytes |
dsireadbuf |
12 (min 6) |
Scale factor: DSI read buffer = server_quantum × dsireadbuf |
server quantum |
DSI_SERVQUANT_DEF |
DSI server quantum in bytes (typically 1 MB) |
How tcpsndbuf / tcprcvbuf work: When non-zero, these are applied per-connection via setsockopt(SOL_SOCKET, SO_SNDBUF/SO_RCVBUF) on the client DSI socket in afp_over_dsi() in etc/afpd/afp_dsi.c. This overrides the OS kernel default socket buffer sizes for the AFP session’s TCP connection. When set to 0 (default), the kernel’s auto-tuning applies — most modern kernels dynamically scale socket buffers based on connection bandwidth-delay product. Setting explicit values disables this auto-tuning for the socket and uses the specified fixed size instead.
OS-specific behavior:
| OS | Auto-tuning sysctls | Max buffer sysctl | Notes |
|---|---|---|---|
| Linux | net.ipv4.tcp_wmem / net.ipv4.tcp_rmem (3-tuple: min, default, max) |
net.core.wmem_max / net.core.rmem_max |
Kernel doubles the requested value internally for overhead accounting; SO_SNDBUF/SO_RCVBUF are clamped to wmem_max/rmem_max |
| macOS | net.inet.tcp.sendspace / net.inet.tcp.recvspace (default values); auto-scaling via net.inet.tcp.doautorcvbuf/net.inet.tcp.doautosndbuf |
kern.ipc.maxsockbuf |
Setting explicit SO_SNDBUF/SO_RCVBUF disables auto-scaling for that socket |
| FreeBSD | net.inet.tcp.sendbuf_auto / net.inet.tcp.recvbuf_auto (boolean, default on); net.inet.tcp.sendspace / net.inet.tcp.recvspace (defaults) |
kern.ipc.maxsockbuf |
Explicit setsockopt disables auto-tuning for that socket; values clamped to maxsockbuf |
For high-latency or high-bandwidth links, larger values (e.g., 131072–524288) can improve throughput by allowing more in-flight data without TCP window stalls. On most local networks, leaving at 0 (auto-tuning) is optimal.
12. Authentication Configuration
UAM Loading
Configured in [Global], loaded in configinit() via auth_load():
[Global]
uam list = uams_dhx.so uams_dhx2.so uams_pam.so uams_guest.so
uam path = /usr/lib/netatalk
Kerberos/GSSAPI
[Global]
uam list = uams_gss.so
k5 service = afpserver
k5 realm = EXAMPLE.COM
k5 keytab = /etc/krb5.keytab
The k5 keytab option sets the KRB5_KTNAME environment variable via putenv().
Guest Access
[Global]
guest account = nobody ; Unix account for guest sessions
13. Logging Configuration
Logging is initialized early in afp_config_parse() via setuplog() to enable error reporting during the rest of config parsing.
Log Level Format
[Global]
log level = default:note ; Single component
log level = default:info afpd:debug ; Multiple components
log level = default:debug9 ; Maximum verbosity
Log Destination
[Global]
log file = /var/log/netatalk.log ; Log to file
; log file = ; Default: syslog
log microseconds = yes ; Include μs timestamps (default: yes)
14. FCE (File Change Event) Configuration
Parsed in configinit() in afp_config.c:
| Config Key | Default | Description |
|---|---|---|
fce listener |
NULL |
UDP endpoint for FCE events |
fce coalesce |
NULL |
Event coalescing configuration |
fce events |
NULL |
Which events to send |
fce version |
1 |
FCE protocol version (1 or 2) |
fce ignore names |
".DS_Store" |
File names to ignore |
fce ignore directories |
NULL |
Directories to ignore |
fce notify script |
NULL |
Script to run on events |
fce holdfmod |
60 |
Seconds to hold file modification events |
fce sendwait |
0 |
Milliseconds between UDP event sends |
15. struct afp_options Key Fields
The struct afp_options is embedded in AFPObj at options. Defined in globals.h. Key field → config option mappings:
struct afp_options {
int connections; // "max connections" (default 200)
int tickleval; // "tickleval" (default 30)
int timeout; // "timeout" (default 4)
int flags; // Bitmask from boolean [Global] options
int dircachesize; // "dircache size" (default 8192)
int dircache_mode; // "dircache mode" (0=lru, 1=arc)
int dircache_validation_freq; // "dircache validation freq" (default 1)
int dircache_rfork_budget; // "dircache rfork budget" (KB, default 0)
int dircache_rfork_maxentry; // "dircache rfork maxsize" (KB, default 1024)
int sleep; // "sleep time" (hours → tickles)
int disconnected; // "disconnect time" (hours → tickles)
int fce_fmodwait; // "fce holdfmod" (seconds)
int fce_sendwait; // "fce sendwait" (ms)
unsigned int tcp_sndbuf; // "tcpsndbuf"
unsigned int tcp_rcvbuf; // "tcprcvbuf"
unsigned char passwdbits; // "save password" / "set password"
unsigned char passwdminlen; // "passwd minlen"
uint32_t server_quantum; // "server quantum"
int dsireadbuf; // "dsireadbuf" (scale factor, min 6)
char *hostname; // "hostname" or gethostname()
char *listen, *interfaces; // "afp listen", "afp interfaces"
char *port; // "afp port"
char *Cnid_srv, *Cnid_port; // "cnid server" (host:port)
char *configfile; // -F cmdline or _PATH_CONFDIR "afp.conf"
char *uampath, *uamlist; // "uam path", "uam list"
char *guest, *loginmesg; // "guest account", "login message"
char *k5service, *k5realm; // "k5 service", "k5 realm"
char *k5keytab; // "k5 keytab"
char *unixcodepage; // "unix charset"
char *maccodepage; // "mac charset"
char *volcodepage; // "vol charset"
char *servername; // "server name"
char *mimicmodel; // "mimic model"
char *logconfig, *logfile; // "log level", "log file"
bool log_us_timestamp; // "log microseconds"
int splice_size; // "splice size" (default 64K)
uint64_t sparql_limit; // "sparql results limit"
};
16. Command-Line Options
afp_options_parse_cmdline() in afp_options.c accepts:
| Flag | Effect |
|---|---|
-d |
Set OPTION_DEBUG in cmdlineflags |
-F configfile |
Override config file path |
-v |
Show version and compiled-in paths |
-V |
Show extended version info (features, backends) |
-h |
Show usage |
17. Complete Configuration Example
[Global]
; Server identity
hostname = fileserver
server name = AFP Server
mimic model = Xserve
; Network
afp listen = 0.0.0.0:548
afp port = 548
; Authentication
uam list = uams_dhx2.so uams_dhx.so
guest account = nobody
; Logging
log level = default:info
log file = /var/log/netatalk.log
; Performance
max connections = 200
dsireadbuf = 12
tcpsndbuf = 0
tcprcvbuf = 0
; Cache tuning
dircache size = 8192
dircache mode = lru
dircache validation freq = 1
dircache rfork budget = 0
dircache rfork maxsize = 1024
; Charsets
unix charset = UTF8
mac charset = MAC_ROMAN
; Service discovery
zeroconf = yes
; Spotlight
spotlight = no
[Homes]
basedir regex = /home
home name = $u's home
[SharedDocs]
path = /srv/documents
volume name = Shared Documents
ea = sys
unix priv = yes
veto files = .DS_Store/.AppleDouble/._*
[TimeMachine]
path = /srv/timemachine
time machine = yes
vol size limit = 1048576
18. Configuration Reload
Volume configuration can be reloaded at runtime when the master afpd process receives SIGHUP. The reload flow in the parent event loop (main() in etc/afpd/main.c):
reloadconfigflag set by signal handler- Free existing DSI listeners via
configfree() - Re-parse
[Global]viaafp_config_parse() - Re-initialize listeners via
configinit() - Send
SIGHUPto all children viaserver_child_kill() - Children call
load_volumes()which detects the changed config file timestamp
Key behavior: Once a volume is loaded in a child process, its options are never changed — they are only deleted and recreated on config reload. This is enforced in creatvol() which checks for existing volumes by name and returns the existing struct vol if still present.
Footnotes
This is a mirror of the Netatalk GitHub Wiki
Last updated 2026-04-06