ExpressVPN – Hourly random smart server reconnect (Linux)

Status: CURRENT — MAJOR 2026 UPDATE
Last reviewed: 31 August 2026
Applies to: Current ExpressVPN Linux app / CLI using expressvpnctl

The original idea still works, but ExpressVPN replaced its old Linux CLI in late 2025. Commands such as expressvpn list all, expressvpn refresh and expressvpn connect belong to the old client. The current CLI is expressvpnctl.

The original version of this article forced ExpressVPN to disconnect, choose another server and reconnect every hour.

I originally borrowed the idea from an Ubuntu 101 article and then tweaked it with suggestions from commenters. The old method did the job, but the ExpressVPN Linux app has changed enough that copying the old script today will mostly result in Linux staring back at you like you’ve personally offended it.

Do you actually need to change VPN server every hour?

Probably not.

Changing servers every hour doesn’t magically make the VPN more private. It also interrupts existing connections, changes your public IP and can annoy websites/services that suddenly see you teleport from one location to another.

But there are still legitimate reasons to automate a reconnect — for example, working around a server that gets shitty after running for a long time, periodically refreshing the public IP, or because you simply enjoy making networking unnecessarily complicated. I can’t judge; look at the rest of this website 🙂

What changed in ExpressVPN for Linux?

ExpressVPN introduced a redesigned Linux app in version 5.0.0 in November 2025 and replaced the old command-line interface with a new CLI.

The command now starts with:

expressvpnctl


The current app also has built-in:

  • Smart Location
  • Auto-connect rules
  • Network Lock / kill switch
  • Split tunnelling
  • Background mode for headless CLI use
  • Automatic protocol selection

So we don’t need the old @reboot expressvpn connect cron bodge anymore.

1. Enable ExpressVPN background mode

Current ExpressVPN requires either the GUI to be running or background mode to be enabled before CLI connection commands can work unattended.

For a headless/server-style setup:

expressvpnctl background enable


Check the current CLI options at any time with:

expressvpnctl -h


2. Test Smart Location

ExpressVPN’s current Smart Location chooses a location based on things such as speed and proximity.

Connect explicitly to Smart Location with:

expressvpnctl connect smart


To see the currently available location names:

expressvpnctl get regions


And if you want to manually choose one:

expressvpnctl connect "Australia - Sydney"


Use the exact location name shown by expressvpnctl get regions. Server/location names can change, so don’t assume my example is immortal.

3. Create the hourly Smart Location script

I now keep little user scripts under ~/.local/bin rather than dumping homemade scripts into /usr/sbin.

mkdir -p ~/.local/bin
nano ~/.local/bin/expressvpn-smart-reconnect.sh


Paste:

#!/usr/bin/env bash
set -euo pipefail

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Ask ExpressVPN to select its current Smart Location.
# No explicit disconnect first - just switch/reconnect.
expressvpnctl connect smart


Then make it executable:

chmod +x ~/.local/bin/expressvpn-smart-reconnect.sh


Test it manually:

~/.local/bin/expressvpn-smart-reconnect.sh


Why I don’t disconnect first anymore: the old script deliberately ran disconnect before reconnecting. The current CLI can connect/switch directly to another location. Avoiding a deliberate disconnected window is cleaner, especially on a machine carrying traffic you actually care about.

4. Use a systemd user timer instead of cron

Cron would still work, but systemd timers give better logging and are easier to inspect when something goes cactus.

Create the user service:

mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/expressvpn-smart-reconnect.service


Paste:

[Unit]
Description=Reconnect ExpressVPN using Smart Location

[Service]
Type=oneshot
ExecStart=%h/.local/bin/expressvpn-smart-reconnect.sh


Now create the timer:

nano ~/.config/systemd/user/expressvpn-smart-reconnect.timer


Paste:

[Unit]
Description=Reconnect ExpressVPN every hour

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target


Reload systemd and enable the timer:

systemctl --user daemon-reload
systemctl --user enable --now expressvpn-smart-reconnect.timer


Check it:

systemctl --user status expressvpn-smart-reconnect.timer
systemctl --user list-timers --all | grep expressvpn


And check the reconnect log after it runs:

journalctl --user -u expressvpn-smart-reconnect.service --no-pager -n 50


If this is a headless machine and you want the user timer to keep running when you’re not logged in, enable lingering for that Linux user:

sudo loginctl enable-linger "$USER"


Optional: actually choose a random location

Smart Location is not random. It deliberately chooses what ExpressVPN thinks is the best location. It may happily choose the same location again.

If you genuinely want random rotation, I prefer keeping a small list of locations I am happy to use rather than scraping whatever format expressvpnctl get regions happens to print this month.

First get the current valid names:

expressvpnctl get regions


Then create:

nano ~/.local/bin/expressvpn-random-reconnect.sh


Example:

#!/usr/bin/env bash
set -euo pipefail

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Pick locations YOU are happy to use.
# Confirm current names first with: expressvpnctl get regions
LOCATIONS=(
  "Australia - Sydney"
  "Australia - Melbourne"
)

LOCATION="${LOCATIONS[RANDOM % ${#LOCATIONS[@]}]}"

printf 'Switching ExpressVPN to: %s\n' "$LOCATION"
expressvpnctl connect "$LOCATION"


Make it executable:

chmod +x ~/.local/bin/expressvpn-random-reconnect.sh


If you prefer this mode, change the systemd service’s ExecStart to:

ExecStart=%h/.local/bin/expressvpn-random-reconnect.sh


Then reload it:

systemctl --user daemon-reload
systemctl --user restart expressvpn-smart-reconnect.timer


Yeah, the timer name still says “smart” if you do that. Rename it too if that sort of thing keeps you awake at night. My brain would absolutely notice it six months later and become irrationally annoyed.

Network Lock / kill switch

ExpressVPN’s current Linux CLI has Network Lock enabled by default. It is designed to block traffic when the VPN connection unexpectedly drops.

You can explicitly enable it with:

expressvpnctl set networklock true


For the best experience ExpressVPN recommends leaving the VPN protocol on Automatic unless you have a reason to force something else:

expressvpnctl set protocol auto


Don’t confuse “hourly server switching” with a kill switch. The timer changes location. Network Lock is what protects traffic when the VPN unexpectedly disappears. They solve completely different problems.

Checking your public IP

The old article used expressvpn status. With the new client I prefer simply confirming the outside world sees a different IP:

curl -4 https://ifconfig.me ; echo


Run it before and after a reconnect. If the location/server actually changed, the public IP will normally change too.

Old method — archived

ARCHIVED — OLD EXPRESSVPN LINUX CLI

The original article used the pre-5.x Linux CLI. These commands are preserved so old search results and installations make sense, but don’t use them with the current client.

The original connection-at-boot cron entry was:

MAILTO=""
@reboot expressvpn connect


And the old randomisation script did roughly this:

expressvpn disconnect
expressvpn refresh

VPN=$(expressvpn list all | tail -n +4 | awk '{ print $1 }' | shuf -n 1)

expressvpn connect "$VPN" 


Finally it ran every hour with:

0 */1 * * * /usr/sbin/smartexpressvpn.sh


The cron concept wasn’t wrong. The commands underneath it simply got replaced when ExpressVPN rebuilt the Linux app.

A note about the original random script

The old script parsed human-readable command output using tail, awk, grep and shuf. It even relied on specific column positions.

That kind of script is fine until the application changes one heading or adds one bloody space and suddenly your “VPN location” variable contains a decorative column title.

The current version either asks ExpressVPN directly for smart, or chooses from a small explicit list of valid locations. Much less clever. Much less likely to fuck itself.

References

The idea survived; the commands didn’t. Which is pretty much the lifecycle of every Linux how-to eventually 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *