Make Samba Work Right (for Linux & MacOS)

· cyclicircuit's blog

How to configure SAMBA so that it works right with Linux and MacOS

Table of Contents

One of the persistent frustrations in my life is that Samba/SMB has weird issues around character encoding. This has gotten so bad in the past that I simply gave up on it. It works great for Windows, but with Linux and MacOS clients I ended up running into serious issues, particularly because English is not the only language which I use in filenames. Over time, I figured out that there is a series of magical configuration options, buried deep in the Samba documentation that make it work for Linux and MacOS and I don't think it breaks Windows (though I am not sure about that last part).

Why this isn't the default, I have no idea.

Anyway, this is how we unfuck Samba for Linux/MacOS - in the /etc/samba/smb.conf we need to set the global block:

[global]
    dos charset = CP850
    unix charset = UTF-8
    mangled names = no
    server min protocol = SMB3
    client min protocol = SMB3
    multicast dns register = yes

mangled names = no: 8.3 mangling is a Windows-95-era thing nobody should be inflicting on a civilized system. The min protocol lines explicitly disable SMB1 (recent Samba already does this, but it's worth being loud about). And multicast dns register makes the server discoverable by macOS Finder. Samba handles the _smb._tcp registration itself once that's on; if you want a recognizable Finder icon, drop an Avahi service file alongside it for the device-info record:

1<!-- /etc/avahi/services/smb.service -->
2<?xml version="1.0" standalone='no'?>
3<!DOCTYPE service-group SYSTEM "avahi.dtd">
4<service-group>
5  <name replace-wildcards="yes">%h</name>
6  <service><type>_device-info._tcp</type><port>0</port>
7    <txt-record>model=RackMac</txt-record>
8  </service>
9</service-group>

The model= value (RackMac, MacPro7,1, Xserve, etc.) is what controls the Finder icon.

And then for every share, we need to configure as follows. This is the ansible jinja2 template I use:

 1[{{ item.name }}]
 2    comment = {{ item.comment }}
 3    path = {{ item.path }}
 4    browseable = {{ 'yes' if item.browseable else 'no' }}
 5    writable = {{ 'yes' if item.writable else 'no' }}
 6    guest ok = no
 7    valid users = {{ item.valid_users }}
 8    create mask = 0744
 9    directory mask = 0755
10    mangled names = no
11    case sensitive = yes
12    default case = lower
13    preserve case = yes
14    short preserve case = yes
15    vfs objects = catia fruit streams_xattr
16    fruit:encoding = native
17    fruit:metadata = stream
18    fruit:resource = stream
19    fruit:posix_rename = yes
20    fruit:veto_appledouble = no
21    fruit:nfs_aces = no
22    fruit:wipe_intentionally_left_blank_rfork = yes
23    fruit:delete_empty_adfiles = yes
24    catia:mappings = 0x22:0xa8,0x2a:0xa4,0x2f:0xf8,0x3a:0xf7,0x3c:0xab,0x3e:0xbb,0x3f:0xbf,0x5c:0xff,0x7c:0xa6

And this is what the final result looks like:

[storage]
    comment = Infosphere storage array
    path = /storage
    browseable = yes
    writable = yes
    guest ok = no
    valid users = cyclicircuit
    create mask = 0744
    directory mask = 0755
    mangled names = no
    case sensitive = yes
    default case = lower
    preserve case = yes
    short preserve case = yes
    vfs objects = catia fruit streams_xattr
    fruit:encoding = native
    fruit:metadata = stream
    fruit:resource = stream
    fruit:posix_rename = yes
    fruit:veto_appledouble = no
    fruit:nfs_aces = no
    fruit:wipe_intentionally_left_blank_rfork = yes
    fruit:delete_empty_adfiles = yes
    catia:mappings = 0x22:0xa8,0x2a:0xa4,0x2f:0xf8,0x3a:0xf7,0x3c:0xab,0x3e:0xbb,0x3f:0xbf,0x5c:0xff,0x7c:0xa6

Case sensitivity: it depends on who your clients are #

This is where Samba is kinda unfixable. For the four case sensitive / default case / preserve case / short preserve case lines above, you have to pick whichever OS is most often the client and optimize for that.

If the share is primarily serving Linux clients, keep those lines as-is. The Linux filesystem underneath is case-sensitive, but by default Samba pretends it isn't, so two paths that differ only in case (Lou.mkv vs lou.mkv) silently collapse to the same file at the SMB layer. That's a data-loss bug waiting to happen during dedup or sync, and it'll bite you in the ass long after you've forgotten about it.

If the share is primarily serving macOS clients, drop those four lines. macOS expects case-insensitive volumes, and a bunch of Mac software (Adobe, some Office templates, Steam) breaks in subtle ways on a case-sensitive backing store. The Linux-side data-loss risk is still there in theory, but in practice Mac clients aren't producing the case-only collisions that cause the problem.

If it's mixed, pick based on whichever side has the workflows you care more about and tell the other side to watch their step. There's no simple answer.

What catia:mappings actually does #

That hex soup maps the characters Windows and macOS won't let you use in filenames to Unicode lookalikes that Linux happily stores:

Win/Mac char Maps to Looks like
" (0x22) 0xa8 ¨
* (0x2a) 0xa4 ¤
/ (0x2f) 0xf8 ø
: (0x3a) 0xf7 ÷
< (0x3c) 0xab «
> (0x3e) 0xbb »
? (0x3f) 0xbf ¿
\ (0x5c) 0xff ÿ
| (0x7c) 0xa6 ¦

So a Mac saving My File: V2 lands on disk as My File÷ V2. Its ugly, but it works across platforms (and honestly, what part of this isn't ugly?).

Linux client mount options #

The other half of the situation is to get the matching CIFS mount options on a Linux client, and these are also non-obvious. You're gonna wanna set these in /etc/fstab:

//server/share /mnt/share cifs \
  vers=3.1.1,\
  credentials=/etc/samba/creds,\
  uid=1000,gid=1000,forceuid,forcegid,\
  file_mode=0644,dir_mode=0755,\
  iocharset=utf8,\
  mapposix,\
  nounix,\
  cache=strict,\
  _netdev 0 0

The non-obvious ones:

Sincerest apologies to all the forum posts where I gathered this information over the years, I simply don't have the links to you anymore. If I did, I would cite them.


2026-06-02 Update: Revised this article based on bugs and quality-of-life issues discovered over time running this config. The notable additions:

last updated: