mullvad manual
Table of contents
Overview
mullvad.el provides an Emacs interface for Mullvad, a privacy-focused VPN service. Rather than switching to a terminal or the Mullvad graphical application, you can connect, disconnect, and check your VPN status without leaving Emacs.
The development repository is on GitHub.
The package is built around the idea that VPN connections are often tied to specific tasks: you connect to a particular city to access a geo-restricted website, and you want the connection to end automatically after a set period. To support this workflow, mullvad.el offers two layers of indirection:
- City-based connections. You maintain an association list mapping city names to specific Mullvad server identifiers. When you connect, you choose a city and the package selects the corresponding server.
- Website-based connections. You maintain a second association list mapping website names to cities. When you need to access a particular website, you choose it by name and the package resolves the chain: website to city to server.
Timed disconnections are a first-class feature. Every connection can optionally include a duration, after which the package automatically disconnects. This is especially useful for programmatic usage, where you want a VPN connection to last only as long as the operation that needs it.
The package requires the mullvad command-line tool to be installed on your system. It also depends on transient for its interactive menu.
Prerequisites
Before using this package, you must:
- Install Mullvad VPN on your system.
- Log in via the terminal:
mullvad account login YOUR_ACCOUNT_NUMBER. You can find your account number under “Settings” > “Account” in the Mullvad graphical interface. - Ensure the
mullvadcommand-line tool is on yourexec-path. The package attempts to locate it automatically when loaded; if it cannot be found, you must setmullvad-executablemanually (Executable path).
Installation
Manual
Clone the repository and add it to your load-path:
(add-to-list 'load-path "/path/to/mullvad")
(require 'mullvad)
With use-package
;; with vc (Emacs 30+)
(use-package mullvad
:vc (:url "https://github.com/benthamite/mullvad"))
;; with elpaca
(use-package mullvad
:ensure (:host github :repo "benthamite/mullvad"))
;; with straight
(use-package mullvad
:straight (:host github :repo "benthamite/mullvad"))
;; with quelpa
(use-package mullvad
:quelpa (mullvad :fetcher github :repo "benthamite/mullvad"))
User options
Executable path
The user option mullvad-executable specifies the path to the mullvad command-line tool. It defaults to the result of executable-find at load time, which means it will be set automatically if mullvad is on your exec-path.
If the executable is installed in a non-standard location, or if executable-find fails to locate it (in which case the default is an empty string), you should set this option manually:
(setopt mullvad-executable "/usr/local/bin/mullvad")
Every command validates this path before use. If the file does not exist or is not executable, an error is signaled immediately rather than producing a confusing shell error downstream.
Server mappings
The core of the package’s routing logic lives in two association lists.
The user option mullvad-cities-and-servers maps human-readable city names to Mullvad server identifiers. Each entry is a cons cell of two strings. The default value is an empty list; you must populate it for the connect commands to work. For example:
(setopt mullvad-cities-and-servers
'(("London" . "gb-lon-wg-001")
("Madrid" . "es-mad-wg-101")
("New York" . "us-nyc-wg-301")
("Sao Paulo" . "br-sao-wg-202")))
To discover the available server identifiers, use M-x mullvad-list-servers (Listing servers).
The user option mullvad-websites-and-cities adds a second level of indirection, mapping website names to the city names defined in mullvad-cities-and-servers. This is useful when you regularly access geo-restricted services and want to select them by name rather than remembering which city to use:
(setopt mullvad-websites-and-cities
'(("Criterion Channel" . "New York")
("Library Genesis" . "London")
("HathiTrust" . "New York")))
When you connect via a website name, the package looks up the corresponding city in mullvad-websites-and-cities, then resolves that city to a server through mullvad-cities-and-servers.
Connection durations
The user option mullvad-durations controls the list of predefined duration choices (in minutes) presented when the user is prompted for a connection duration. The default value is nil, which means no predefined choices are offered and the user must type a duration manually each time.
When set to a list of integers, these values appear as completion candidates:
(setopt mullvad-durations '(1 5 10 30 60 120))
The user can still type a custom duration not in the list. Leaving the prompt blank (pressing RET without input) means the connection will persist indefinitely until manually disconnected.
This option affects all commands that prompt for a duration, including mullvad-connect-to-city and mullvad-connect-to-website when called interactively (Connecting).
Silent mode
The user option mullvad-silent controls whether the package emits status messages after connecting or disconnecting. The default value is nil, meaning messages are displayed in the echo area.
Set it to t to suppress all status messages:
(setopt mullvad-silent t)
This is particularly useful for programmatic usage, where you call connection functions from Lisp code and do not want messages cluttering the echo area. Note that individual commands also accept a SILENTLY argument, which provides per-call control without changing the global setting.
Commands
Connecting
The package provides three commands for establishing a VPN connection, each offering a different level of specificity.
The command mullvad-connect is the general entry point. It first prompts you to choose a connection type—either “city” or “website”—and then delegates to the appropriate specialized command.
The command mullvad-connect-to-city prompts for a city from mullvad-cities-and-servers, resolves it to a server identifier, and connects. It then prompts for a duration in minutes (Connection durations). If you are already connected to the requested server, the command skips the connection step and proceeds directly to setting (or resetting) the disconnect timer.
The command mullvad-connect-to-website prompts for a website from mullvad-websites-and-cities, resolves it to a city, and delegates to mullvad-connect-to-city. This is the most convenient option when you know which service you need to access but do not want to remember the server geography.
All three commands accept optional arguments for non-interactive use. For example, to connect to a website programmatically with a 30-minute timeout and no status messages:
(mullvad-connect-to-website "Criterion Channel" 30 'silently)
Disconnecting
The command mullvad-disconnect terminates the current VPN connection immediately. It also cancels any pending disconnect timer. If you are not currently connected, the command does nothing beyond displaying the status.
The command mullvad-disconnect-after sets a timer to disconnect after a specified number of minutes. It prompts for a duration using the same mechanism as the connect commands (Connection durations). If a timer is already running, it is replaced with the new one. This command signals an error if you are not currently connected.
Smart connection toggle
The command mullvad-dwim (“do what I mean”) checks the current connection state and acts accordingly. If you are connected, it disconnects. If you are disconnected, it calls mullvad-connect interactively, prompting you for a connection type, server, and duration.
This is a convenient command to bind to a global key for quick VPN toggling.
Checking status
The command mullvad-status displays the current Mullvad VPN status in the echo area. If a disconnect timer is running, the remaining time is appended to the status message (e.g., “Connected to gb-lon-wg-001. Disconnecting in 24 minutes, 30 seconds.”).
Listing servers
The command mullvad-list-servers fetches the complete list of available Mullvad relay servers and displays them in a dedicated *Mullvad Servers* buffer. The buffer is set to special-mode, making it read-only and providing the standard key bindings for navigation (q to quit, etc.).
Use this command to discover server identifiers when populating mullvad-cities-and-servers (Server mappings).
Functions
Connection state
The function mullvad-is-connected-p returns t if the VPN is currently connected and nil otherwise. It works by invoking the mullvad status command and checking the output for the word “Connected”.
The functions mullvad-ensure-connected and mullvad-ensure-disconnected poll the connection state in a loop, waiting up to two seconds for the VPN to reach the expected state. They are called internally after connect and disconnect operations to ensure the command has taken effect before proceeding. This is necessary because the Mullvad CLI returns immediately while the connection is still being established.
Command execution
The function mullvad-run-command is the low-level interface for invoking the Mullvad CLI. It accepts any number of string arguments, shell-quotes each one, and executes the resulting command. All arguments are passed through shell-quote-argument to prevent command injection.
The function mullvad-shell-command wraps mullvad-run-command with error handling and optional message suppression. If the command output begins with “Error” or “error:”, an Emacs error is signaled. If the SILENTLY argument is non-nil (or mullvad-silent is t), messages are suppressed during execution (Silent mode).
Server resolution
The function mullvad-get-server resolves a city or website name to a server identifier. It accepts a CONNECTION argument (the symbol city or website) and an optional SELECTION string. If SELECTION is nil, it prompts interactively with completing-read, requiring a match against the configured alist.
If the selection does not appear in the corresponding association list, a user-error is signaled. This prevents the package from silently passing invalid server identifiers to the Mullvad CLI.
Timer management
The package maintains at most one disconnect timer at a time, stored in the variable mullvad-timer.
The function mullvad-set-timer creates a new timer that will call mullvad-disconnect after the specified number of minutes.
The function mullvad-cancel-timers cancels the running timer, if any, and resets the variable to nil. It is called automatically by both mullvad-disconnect and mullvad-disconnect-after before setting a new timer.
The function mullvad-get-time-until-disconnect returns a human-readable string describing the remaining time (e.g., “24 minutes, 30 seconds”), or nil if no timer is active. This is used by mullvad-status (Checking status).
Transient menu
The package provides a transient menu accessible via M-x mullvad. It groups all interactive commands under descriptive headings:
| Key | Command | Description |
|---|---|---|
c | mullvad-connect-to-city | Connect by city |
w | mullvad-connect-to-website | Connect by website |
n | mullvad-disconnect | Disconnect now |
l | mullvad-disconnect-after | Set a timed disconnect |
d | mullvad-dwim | Toggle connection |
s | mullvad-status | Display current status |
r | mullvad-list-servers | List available servers |
The transient menu is the recommended entry point for interactive use. You might bind it to a convenient key:
(global-set-key (kbd "C-c v") #'mullvad)
Programmatic usage
The package is designed to be called from Lisp code as well as interactively. A common pattern is to wrap an operation that requires a specific VPN location in an :around advice:
(defun my-set-mullvad-for-gemini (orig-fun &rest args)
"Connect to a US server before calling ORIG-FUN with ARGS."
(when (eq gptel-model 'gemini-pro)
(require 'mullvad)
(mullvad-connect-to-website "Gemini" 10 'silently))
(apply orig-fun args))
(advice-add 'gptel-curl-get-response :around #'my-set-mullvad-for-gemini)
The key points for programmatic use are:
- Pass explicit city/website and duration arguments to avoid interactive prompts.
- Pass a non-nil SILENTLY argument (or set
mullvad-silenttot) to suppress echo area messages. - The connect commands are idempotent: if you are already connected to the requested server, no reconnection occurs.
Troubleshooting
“Mullvad executable not found or is not executable”
This error means mullvad-executable does not point to a valid executable. Verify that the Mullvad CLI is installed and check the value of the variable:
(message "%s" mullvad-executable)
If the path is empty or wrong, set it manually (Executable path). On macOS, the CLI is typically installed at /usr/local/bin/mullvad. On Linux, it is usually at /usr/bin/mullvad.
“No server configured for …”
This error means you selected a city or website that does not appear in the corresponding association list. Check mullvad-cities-and-servers or mullvad-websites-and-cities and ensure the selection matches an existing key (Server mappings).
Connection does not complete
After issuing a connect command, the package polls the connection state for up to two seconds. If the connection has not been established by then, the package proceeds without error, but subsequent commands may behave unexpectedly. This can happen if the Mullvad daemon is not running or if the network is unusually slow. Verify that the daemon is running with mullvad status in a terminal.