Let’s be real: Google doesn't want you using mbsync with Gmail and OAuth2. They want you in the browser. They want you seeing the ads, using the "Smart Compose," and staying firmly within the ecosystem. But if you’re a terminal junkie or a Mutt/NeoMutt user, that’s just not how you roll. You want your mail local. You want it fast. You want it greppable.
The problem is that back in May 2022, Google officially killed off "Less Secure Apps." That was the final nail in the coffin for simple password authentication. Now, you’re forced into the world of OAuth2, which is—frankly—a massive headache for a tool as minimalist as isync (the actual package name for the mbsync executable). If you’ve been staring at SASL(0): successful success or "Authentication failed" errors for the last three hours, you aren't alone. It's a rite of passage.
Why the old ways died
We used to just generate an "App Password" and call it a day. It worked. It was simple. But Google’s push toward OAuth2 isn't just a whim; it’s a fundamental shift in how they handle tokenized access. Instead of giving a third-party app your password (or a proxy for it), you’re giving it a refresh token.
This is objectively more secure. If your laptop gets swiped, you revoke the token, not your whole Google identity. But mbsync wasn't exactly built with a "Login with Google" button. It’s a CLI tool from a different era. To bridge that gap, you need a helper script—usually something like cyrus-sasl-xoauth2 or a Python script to fetch the initial token. Honestly, it’s a lot of moving parts for something that should be simple.
✨ Don't miss: Gemini Versions: What Most People Get Wrong About Google's AI Models
The Secret Sauce: Google Cloud Console
You can't just tell mbsync to "go." You have to register yourself as a developer first. It sounds ridiculous, but you’re basically creating your own "app" just so you can read your own email.
Go to the Google Cloud Console. Create a project. Call it "MyEmail" or whatever. You need to enable the Gmail API, but here’s where everyone trips up: the OAuth Consent Screen. You don't need to verify the app with Google (which takes weeks and costs money). Just set the "User Type" to External and the "Publishing Status" to Testing. Add your own email as a test user. If you skip this, you’ll get a "403 Access Denied" faster than you can blink.
Once you have your Client ID and Client Secret, you're halfway there. Keep these safe. They are the keys to your inbox.
Configuring mbsync for the modern age
The ~/.mbsyncrc file is picky. One wrong space and it fails silently. When setting up mbsync with Gmail and OAuth2, your IMAPAccount section needs to look very specific.
IMAPAccount gmail
Host imap.gmail.com
User yourname@gmail.com
PassCmd "python3 ~/bin/mbsync-get-token.py"
AuthMechs XOAUTH2
SSLType IMAPS
Notice the PassCmd. This is the heavy lifter. Since mbsync can't handle the OAuth2 handshake itself, it asks a script to provide the token. Most people use a variant of the gmail-oauth2-tools provided by Google’s own GitHub, or more likely, the community-maintained Python scripts found in various Arch Wiki entries.
The Token Lifecycle
You run the script once manually. It gives you a URL. You paste that URL into a browser, log in, and Google gives you an authorization code. You paste that back into the terminal. The script then exchanges that code for a Refresh Token.
This refresh token is gold. It lives in a hidden file on your hard drive. Every time mbsync runs, the PassCmd fires off, uses that refresh token to grab a temporary Access Token, and hands it to mbsync. It happens in milliseconds. It’s seamless once it works, but the initial setup feels like you’re trying to hotwire a car.
Common Pitfalls: Why your sync is failing
If it’s not working, check your labels. Gmail doesn't use "folders" in the traditional sense; it uses labels. This causes a mess with mbsync.
- The "All Mail" Trap: If you sync "All Mail," "Inbox," and "Sent," you are downloading three copies of every sent email. Your disk space will vanish. Use the
Patternsdirective to exclude[Gmail]/All Mail. - The "Sent" Duplicate: Gmail automatically puts a copy of any mail sent via their SMTP into your "Sent" folder. If your mail client (like Mutt) also saves a copy to the IMAP "Sent" folder, you’ll see double. Disable local "Sent" saving in your mail client.
- The UID Validity Error: This is the bane of
isyncusers. If the remote UID store changes,mbsyncwill refuse to sync to prevent data loss. Usually, this happens if you mess with Gmail’s "labels" settings online. You’ll often have to delete your local.mbsyncstate files and re-sync. It’s annoying. It takes time.
Security Nuances Nobody Tells You
Storing your Client Secret and Refresh Token in plain text is a bad idea. Seriously. If someone gets access to your machine, they have full access to your Gmail.
Expert move: use a password manager with a CLI. Use Pass (the standard unix password manager) or GPG to encrypt the token file. You can then change your PassCmd to something like pass show gmail-token | head -n 1. It adds a layer of encryption that keeps your private data private, even if your dotfiles end up on GitHub by accident.
Also, keep an eye on the scopes. When you authorize your "app," only give it the https://mail.google.com/ scope if you absolutely have to. Most people don't realize there are more granular scopes, but for mbsync to work properly across all folders, the full "mail" scope is usually the only path that doesn't lead to weird permission errors.
The Performance Reality
Is it worth it?
Synchronizing mbsync with Gmail and OAuth2 is significantly faster than using IMAP directly within a client. Why? Because mbsync stays in the background. It does the heavy lifting while you're doing other things. When you open your mail client, the mail is already there on your NVMe drive. It’s instant. No "Fetching headers..." spinning icons. No lag.
👉 See also: The Real Story of What God Hath Wrought and Why We Still Care
However, Gmail’s IMAP implementation is... quirky. It’s not standard IMAP. They use a proprietary layer that mimics IMAP, which is why things like "labels as folders" feel so janky. Occasionally, you’ll hit rate limits if you try to sync 50GB of mail in one go. Google will throttle you. If that happens, just walk away. Let it run overnight.
Actionable Steps to Get This Running Today
Don't try to do this all at once. You'll get frustrated.
- Step 1: The Cloud Console. Create your project and get your Client ID and Secret. Do this first because sometimes Google takes a few minutes to propagate these credentials.
- Step 2: Install the SASL Plugin. On Arch, it's
cyrus-sasl-xoauth2. On Debian/Ubuntu, you might have to build it from source or find a PPA. Without this,mbsyncwon't even know what theXOAUTH2mechanism is. - Step 3: Test the Token Script. Forget
mbsyncfor a second. Just run your Python script and make sure it can successfully exchange a code for a refresh token. If this fails,mbsynchas no chance. - Step 4: Minimalist Config. Start with a tiny sub-folder. Don't try to sync your 15-year archive of "Promotions" and "Social" updates. Just sync a "Test" label.
- Step 5: Automation. Once it works, put it in a systemd timer or a cron job.
mbsync -aevery 5 minutes is usually plenty.
The shift to OAuth2 was a hurdle for the open-source community, but the tools have caught up. It’s stable now. Once the tokens are exchanged and the config is set, you can go months without ever thinking about it again. You get to keep your local workflow, and Google gets their security tokens. Everyone wins, even if the setup process feels like a weekend project you didn't ask for.