Guide: split-Protonmail (offline send/receive qubes + pm bridge vm)


Hello all.

The goal of this guide is to provide you with a split-protonmail setup made of a minimal mail template and three AppVMs: protonmail-bridge, mail-receive, and mail-send.

This will allow you to effectively separate incoming and outgoing mail (into non-networked qubes), and your protonmail credentials (security by compartmentalization).

This guide was inspired by a Reddit post to which I made some modifications, since the configuration proposed in the original post didn’t quite work for me.

I hope this guide can help some of you.

Note 1: I’m not an expert, so I’m sharing what worked for me. If you have suggestions or modification proposals, feel free to post them.
Also, feel free to let me know if I’ve made any mistake so I can update this post accordingly.

Note 2: Protonmail Bridge is currently only available to Protonmail paid accounts.

1. Mail template

Note 1: if you don’t want to create a dedicated template, just make sure that the required software is installed in your template of choice. Personally, I prefer to create a template for each purpose.

Note 2: you can take this a step further by creating a template for Protonmail bridge, and another one for Thunderbird. For simplicity, in this example, I use only one template for both purposes.

Note 3: I’m using a debian-11-minimal template, I haven’t tested this setup with Fedora.

  1. Install the official minimal template as instructed in the Qubes Documentation: Minimal templates | Qubes OS
  2. Clone the minimal template:
[user@dom0]$ qvm-clone debian-11-minimal debian-11-protonmail
  1. Update the new clone:
    Note 1: those of you who set up a apt-cacher-ng qube might need to update the urls of the repositories. Expand the section below for the commands
Howto: Update repo urls to work with `apt-cacher-ng`
[root@debian-11-protonmail]# sed -i 's#https://#http://HTTPS///#g' /etc/apt/sources.list
[root@debian-11-protonmail]# sed -i 's#https://#http://HTTPS///#g' /etc/apt/sources.list.d/*.list
[root@debian-11-protonmail]# apt update && apt full-upgrade -y
  1. Create a packages.txt file, type them or use whatever method you prefer to install the following packages:
[root@debian-11-protonmail]# apt install -y --no-install-recommends $(cat packages.txt)
  1. Download and install Protonmail Bridge, then shutdown the qube:
    Note 1: those of you who set up a apt-cacher-ng qube might need to change the protonmail url from to http://HTTPS///
    Note 2: if you’re using Fedora, you will need to check the Protonmail website for the appropriate package.
[root@debian-11-protonmail]# https_proxy= http_proxy= wget
[root@debian-11-protonmail]# dpkg -i protonmail-bridge_*_amd64.deb && shutdown

2. AppVM: protonmail-bridge

This qube will only run protonmail bridge and will be the only one with a netvm assigned. This example uses sys-whonix, feel free to pick your netvm of choice.

Note 1: Keep in mind that if you pick a different name than protonmail-bridge you will have to adjust some parameters in the following steps.

Note 2: I included a maxmem value that works for me. You can always adjust as needed.

  1. Create the qube:
[user@dom0]$ qvm-create --class=AppVM --template=debian-11-protonmail --label=blue protonmail-bridge
[user@dom0]$ qvm-prefs protonmail-bridge maxmem 900
[user@dom0]$ qvm-prefs protonmail-bridge netvm sys-whonix
  1. Optional: set firewall to Limit outgoing connections to:
  2. Create the policies:
[root@protonmail-bridge]# mkdir -p /rw/usrlocal/etc/qubes-rpc
[root@protonmail-bridge]# echo 'socat STDIO TCP:localhost:1143' > /rw/usrlocal/etc/qubes-rpc/user.protonmail-imap
[root@protonmail-bridge]# echo 'socat STDIO TCP:localhost:1025' > /rw/usrlocal/etc/qubes-rpc/user.protonmail-smtp
  1. Optional: add Protonmail Bridge to appmenu
[user@dom0]$ qvm-features protonmail-bridge menu-items protonmail-bridge.desktop
[user@dom0]$ qvm-sync-appmenus protonmail-bridge
  1. Shutdown the VM:
[user@dom0]$ qvm-shutdown protonmail-bridge

3. AppVM: mail-receive

This qube will only be able to receive mail and will not be connected to a netvm.

  1. Create the qube:
[user@dom0]$ qvm-create --class=AppVM --template=debian-11-protonmail --label=green mail-receive
[user@dom0]$ qvm-prefs protonmail-receive maxmem 900
[user@dom0]$ qvm-prefs protonmail-receive netvm ''
  1. Add the startup command:
[root@mail-receive]# echo 'qvm-connect-tcp ::1143' >> /rw/config/rc.local
  1. Optional: add Thunderbird to appmenu
[user@dom0]$ qvm-features mail-receive menu-items thunderbird.desktop
[user@dom0]$ qvm-sync-appmenus mail-receive
  1. Shutdown the VM:
[user@dom0]$ qvm-shutdown mail-receive

4. AppVM: mail-send

This qube will only be able to send mail and will not be connected to a netvm.

  1. Create the qube:
[user@dom0]$ qvm-create --class=AppVM --template=debian-11-protonmail --label=red mail-send
[user@dom0]$ qvm-prefs protonmail-send maxmem 900
[user@dom0]$ qvm-prefs protonmail-send netvm ''
  1. Add the startup command:
[root@mail-send]# echo 'qvm-connect-tcp ::1025' >> /rw/config/rc.local
  1. Optional: add Thunderbird to appmenu
[user@dom0]$ qvm-features mail-send menu-items thunderbird.desktop
[user@dom0]$ qvm-sync-appmenus mail-send
  1. Shutdown the VM:
[user@dom0]$ qvm-shutdown mail-send

5. dom0 policies

These policies allow communications between the relevant qubes: specifically, the mail-receive qube will be able to fetch email automatically, while the mail-send qube will require an extra user prompt. This set up may or may not be desirable for you, so feel free to experiment.

[root@dom0]# echo "mail-receive protonmail-bridge allow,target=protonmail-bridge" >> /etc/qubes-rpc/policy/user.protonmail-imap
[root@dom0]# echo "mail-send protonmail-bridge ask,default_target=protonmail-bridge" >> /etc/qubes-rpc/policy/user.protonmail-smtp
[root@dom0]# echo "mail-receive @default allow,target=protonmail-bridge" >> /etc/qubes-rpc/policy/qubes.ConnectTCP
[root@dom0]# echo "mail-send @default ask,default_target=protonmail-bridge" >> /etc/qubes-rpc/policy/qubes.ConnectTCP

6. Final configurations

  1. Start protonmail-bridge, login with your Protonmail credentials and make sure that the bridge is set to run on startup (there’s an option in the app settings).
  2. Start mail-receive, add an account on Thunderbird with the credentials provided by Protonmail bridge (NOT your actual Protonmail credentials!). For IMAP use server, port 1143. For SMTP use Connection security: STARTTLS, Normal Password. Then click on Advanced settings to save, and fetch mail.
  3. Start mail-send, add an account on Thunderbird with the credentials provided by Protonmail bridge (NOT your actual Protonmail credentials!). For IMAP use server For SMTP use server, port 1025. Connection security: STARTTLS, Normal Password. Then click Advanced settings to save.
  4. mail-send requires that you approve the Protonmail certificate: you’ll receive a prompt the first time you attempt to send an email.

WaitForSession error?

I’ve fully implemented the protonmail-bridge and mail-receive qubes, but when I try to check mail in Thunderbird nothing happens. journalctl in the mail-receive qube shows the following error:

host qubes.WaitForSession-dom0[2238]: Cannot access user instance remotely.

while all seems good from dom0, which only shows:

dom0 qrexec-policy-daemon[2495]: qrexec: qubes.ConnectTCP+1143: mail-receive → @default: allowed to protonmail-bridge

There was a fix for the WaitForSession error in discussions of the split-browser, but I don’t see how the fix may apply here.

Anyone have ideas on how to proceed?

This error may be due to using whonix templates. Switching to debian-minimal templates alleviated the issue.

Additional notes:

  • Toggle Alternative routing (Settings > Advanced settings) to the Off position to prevent the Proton Mail Bridge from using Google DNS servers, among others. See this Reddit post for more detail.

  • Using the new qrexec policy syntax will futureproof the setup. For example, as an alternative to the third dom0 policy above, I used::

[root@dom0]# echo "qubes.ConnectTCP * mail-receive @default allow target=protonmail-bridge" >> /etc/qubes/policy.d/30-user.policy

I found your guide very helpfull, but why did you decide that spliting recive and send is more secure?

Security by compartmentalization. Say you receive a sensitive document, if you were to use a single vm for sending/receiving, that document could be exfiltrated. With the split setup it has nowhere to go.

1 Like

Can you help me a bit? I done everything like you did step by step but I can’t recive email or send email in mail-receive and mail-send. Is there anything that changed between April and now?

Not that I know of. Did you download the latest version of protonmail bridge?

Also, in dom0 check journalctl -r for related messages. If you have misconfigured policies, you’ll see errors there when you try to send/receive.

I done everything with the up-to-date versions. One thing I did different was using the debian-11 non minimal template, and I have them all with protonmail infront(protonmail-send, protonmail-receive, protonmail-bridge) so it would be a lot easier to remember they are related and I renamed everything in the config files to match.

I’m sure the bridge works as the error is given by qubes(dennied access is what I get in send, but in recive just thunderbird says it can’t connect)

Where exactly do you see this error?

Please check journalctl -r when you try to send/receive and report related messages here.

The receive qube had the wrong port set by mistake, but the send one gets Denied Qubes.ConnectTCP+1025

I’m having the same issue. There seems to be something to do with the following entry in qubes.ConnectTCP:

mail-send @default ask,default_target=protonmail-bridge

journalctl -r shows that the request is being denied because “policy define ‘ask’ action … but no target is available to choose from”.

This is weird, as the syntax seems to be all right, and the default_target is correctly specified.

If I change the mail-send policy to “allow, target=protonmail-bridge”, then it works correctly, but there’s no prompt of course. I’m fine with it, but others may not.

If I keep the “ask” policy and provide no default_target, the prompt also works correctly, but then I have to type/select the protonmail-bridge vm every time.

The issue seems to be related with the default_target parameter, but I’m not sure what can it be, for I have other policies for other services where I use “ask, default_target=…” and everything works as expected.

Any ideas?

another minor issue I discovered… If you move an email from the inbox to a local folder and/or delete the email from the inbox, the email will still persist on the protonmail servers in the all mail folder. So there is no way to remove email from the server using only the bridge, one has to log into webmail and manually remove it from the server.

I wouldn’t call it an issue since that’s how it works by design: the receiving vm doesn’t have internet connectivity.

1 Like

Updating bridge to v. 3.0.x

Proton is now hosting the bridge download under their new url, which causes an error when downloading via proxy. For step 5 of 1. Mail template above, with the latest update one should now use the following (edit for current version, 3.0.21-1 as of this post):

[root@debian-11-protonmail]# https_proxy= http_proxy= wget
[root@debian-11-protonmail]# dpkg -i protonmail-bridge_*_amd64.deb && shutdown

Version 3.0.x of the bridge also requires an additional package:

apt-get install libopengl0

After updating, Thunderbird will hang with the message “Checking mail server capabilities”. To fix this, copy the IMAP password from the Mailbox details section of the protonmail bridge app and reset the certificate as follows: (source: reddit)

From Thunderbird, to remove the certificates, go to Menu → Settings → Privacy & Security → Manage Certificates at the bottom. From here, on the “Servers” tab, remove the entries for, and then restart Thunderbird, and click “Get Messages”. You should be prompted to approve the new certificates.


Thanks for writing this up, it was super helpful! I’m hoping you can help me understand one aspect of the policy, and I also have some notes about setting this up on Fedora.

Also, the firewall rules have changed with Proton’s domain switch. I only added to the outgoing connections and everything is working fine.


I don’t understand why the ConnectTCP rules have @default as the target instead of protonmail-bridge. I tried it with the latter and the connection was denied. The notification didn’t specify the target, it was like it was an empty string or something. Does this have to do with setting up the connection through socat?

Notes on Fedora

On fedora, the download URL is:

It can be installed with dnf by prepending ./ to the filename so that it knows it’s a local path. It installs dependencies automatically, except gnome-keyring because the keyring program is a user preference. And it seems like the package doesn’t list opengl, probably because most machines will have it anyway. So I had to install 3 packages separately:


Everything else was the same though!

Thanks again for writing up this guide! I think we can update some things so that it’s locked down even more, in particular we can eliminate the need to allow arbitrary TCP connections between the qubes.

The new policy file (using the new fromat described at Qubes Architecture Next Steps: The New Qrexec Policy System | Qubes OS) looks like this, in /etc/qubes/policy.d/30-protonmail.policy:

# Always allow connection to imap. Thunderbird will give you 2 pop-ups every
# time it checks for new messages, which can be annoying, but this could be
# modified to look like the smtp rule if you want to be prompted every time.
# Enabling notify=yes means that you will have visibility into when the requests
# are happening.
# Note that even though the destination vm is listed as *, we are overriding the
# target in the options. This means that the client can send "default" as the target
# qube and connect correctly.
user.protonmail-imap * protonmail-receive * allow target=protonmail-bridge notify=yes
# Ask for sending mail. This will only happen once for each mail you send which is
# much more manageable.
user.protonmail-smtp * protonmail-send    * ask   default_target=protonmail-bridge
# Finally, make sure that all other uses of these services are denied.
user.protonmail-imap * *                  * deny
user.protonmail-smtp * *                  * deny

Next, we need to update the callbacks in the bridge qube. The only change here is that they need the hashbang added to the top, and they also need to be marked as executable:

echo "#!/bin/sh" > /rw/usrlocal/etc/qubes-rpc/user.protonmail-smtp
echo "socat STDIO TCP:localhost:1025" >> /rw/usrlocal/etc/qubes-rpc/user.protonmail-smtp
echo "#!/bin/sh" > /rw/usrlocal/etc/qubes-rpc/user.protonmail-imap
echo "socat STDIO TCP:localhost:1143" >> /rw/usrlocal/etc/qubes-rpc/user.protonmail-imap
chmod a+x /rw/usrlocal/etc/qubes-rpc/user.protonmail-{smtp,imap}

Finally, we need to update the config files in the receive and send VMs. For receiving:

echo 'socat TCP-LISTEN:1143,fork EXEC:"qrexec-client-vm default user.protonmail-imap"' >> /rw/config/rc.local

And for sending:

echo 'socat TCP-LISTEN:1025,fork EXEC:"qrexec-client-vm default user.protonmail-smtp"' >> /rw/config/rc.local

The fork option to TCP-LISTEN makes it so that socat will run the EXEC every time a new connection is made on that port. The EXEC gets data from the bridge qubes through the qrexec framework instead of using TCP directly (though of course, thunderbird and the bridge are still using tcp, but this communication stays internal to their respective qubes).

This also has the benefit that you don’t have to start the bridge qube manually, because it will be autostarted when the qrexec request is made.