I have written about this problem twice before, and I am writing about it a third time because the answer keeps getting better.

In 2019 it was Dataplicity. In 2024 it was Raspberry Pi Connect, which is first-party, free for personal use, and still where I would send most people who just want a terminal.

But a terminal is only part of what a picture frame has to offer. Your frame is not one service. It is four.

ssh pi@frame.local            the shell
http://frame.local:9000       the web interface
http://frame.local:8384       Syncthing
smb://frame.local/Pictures    the picture share

Every one of those works beautifully — while you are at home. Sit down in a café in Paris and frame.local means nothing at all. A remote shell gives you the first line back. Tailscale gives you all four.

What Tailscale actually does

The simplest way to think about it is this:

Tailscale makes your Raspberry Pi behave as though it were on your own Wi-Fi, even when you are not.

It is a VPN, but not the sort you buy to watch television from another country. Your traffic does not go through a company's server. Tailscale builds a small private network — a tailnet — out of the devices you sign in, and those devices talk to each other directly and encrypted, wherever they happen to be.

Install it on the Pi, install it on your Mac and your phone, sign all of them in to the same account. That is the setup, and after it ssh pi@frame works from anywhere in the world.

Why this fits a picture frame so well

You get every port, not one. A remote shell is a shell. With Tailscale the web interface opens on your phone in Paris exactly as it does on your sofa, Syncthing's page comes with it, and the picture share mounts in Finder.

No router configuration. None. You do not forward port 22. You do not forward 9000. Nothing about your frame — or about the house it lives in — becomes visible from the public internet. This is the part I want to be loudest about. The alternative most people find first is port forwarding, and a Raspberry Pi forwarded to the open internet is how you end up reading about your own frame in somebody's botnet write-up.

The address stops moving. My FRITZ!Box likes to hand the frame a different address after a power cut, and frame.local can take its sweet time to resolve on some phones. A device on a tailnet gets one address and one name, and it keeps both — on your Wi-Fi, on mobile data, at your parents' house, for ever.

Several frames stay straight in your head. livingroom, parents, office, holidayhome. They all just appear in the list.

What Tailscale is not

It is not a replacement for Syncthing. The two answer different questions:

  • Syncthing says: keep this photo folder in step.
  • Tailscale says: let these two computers reach each other.

Syncthing already handles the away-from-home part by itself, through its own relays, so do not rip it out. The two get on well: with both installed, Syncthing will often find a direct path over the tailnet instead of falling back to a relay. Keep syncing photos the way you do now — Tailscale is the administration layer underneath.

It is not a way into somebody else's house. Installing Tailscale on a frame at your mother's house puts the frame on your tailnet. It does not put her network on your tailnet, it does not let you see her laptop, and it does not let her see anything of yours. That only changes if you deliberately turn on subnet routing, which you will not be doing here.

What it costs

Nothing, for what we are doing. The free Personal plan covers up to 6 users and unlimited devices, including everything in this article — MagicDNS, Tailscale SSH, file sending.

Six users is more useful than it sounds: your brother can have his own login on the family tailnet rather than you handing round one password.

Step 1 — Install Tailscale on the frame

SSH into the frame the ordinary way, while you are still at home. If you have never opened a terminal, start here.

ssh pi@frame.local

Then one line, which is the official installer and works on Raspberry Pi OS Lite, Trixie or Bookworm:

curl -fsSL https://tailscale.com/install.sh | sh

Copy that line exactly as it stands: it is the same for everybody. If you came here from my Dataplicity article you may be hunting for a personal code tied to your account. Tailscale works the other way round — the software first, your account second.

And bring it up:

sudo tailscale up --ssh

Now it gets personal. The Pi prints something like this:

To authenticate, visit:

        https://login.tailscale.com/a/f4c21b9e7d8a

That URL is generated there and then, for that Pi. Do not copy mine, and do not expect the same one twice — every frame prints its own, single-use and short-lived.

Read it off your own screen, open it in a browser on your own computer and sign in: Google, Microsoft, GitHub, Apple or a passkey. The Pi is watching the link, so the moment you finish, the command completes by itself and the frame is on your tailnet. There is no password to invent and nothing to type back into the Pi.

I will come back to what --ssh does further down. Leave it there for now.

Check it worked:

tailscale status
tailscale ip -4

The address comes back as something like 100.101.102.103. Every machine on a tailnet gets one from that range, 100.64.0.0/10 — worth recognising when you meet it again further down.

Step 2 — Turn off key expiry. Do not skip this.

Here is the one that will bite you, six months from now, when you have completely forgotten this article.

By default, a device's key expires after 180 days. When it does, the machine drops off the tailnet and has to be signed in again — from the machine itself. On your laptop that is a mild irritation. On a frame screwed to a wall in another country, it is the exact situation you installed Tailscale to avoid.

So, in the Tailscale admin console, on the Machines page: find the frame, click the three dots at the end of its row, and choose Disable key expiry.

The frame now stays signed in indefinitely. This is the setting Tailscale intends for servers and hard-to-reach equipment, and a picture frame is both. Do it for every frame you install, the same afternoon you install it.

Step 3 — Install it on the things you will use

Tailscale needs to be on both ends. There is an app for macOS, Windows, iOS, Android and Linux, and each one wants the same thing: sign in to the same account.

On the iPhone it is a switch in the app and then you forget about it. Leave it on — it is a WireGuard tunnel to your own machines, and it is not routing your ordinary browsing anywhere.

Step 4 — Call the frame by its name

Tailscale runs its own DNS for your tailnet, called MagicDNS, and it is on by default for any tailnet created since October 2022. It gives each machine a name taken from its hostname, so the Pi you called frame in the Raspberry Pi Imager is simply frame.

Which means that, from anywhere:

ssh pi@frame
http://frame:9000
http://frame:8384

No IP addresses, and no .local. That is the bit to notice: frame.local is mDNS and only ever worked on your own network, while frame is MagicDNS and works everywhere.

Every machine also gets a full name, frame.your-tailnet.ts.net, which is what you see in the admin console. Read the next section before you use it in a browser.

One name per machine. A second Pi also called frame gets handed frame-1, which is nobody's idea of a helpful label. Give each one a real name in the Imager when you write the card.

The bit that will trip you up: the frame's Host check

Open http://frame:9000 from Paris and it works. Open http://frame.your-tailnet.ts.net:9000 and you get this instead:

this interface only answers on the frame's own address on your network;
the Host header 'frame.your-tailnet.ts.net:9000' is not one of them

That is not Tailscale. That is picframe3 doing its job, and the message reads like a fault when it is not one.

The frame's web interface has no password — it is meant to be picked up and prodded, not logged in to. What keeps a random web page from using your browser as a way in is a check on the Host header: the frame answers to its own addresses and to plain names, and refuses anything that looks like a domain somebody could register.

In practice:

Address Works? Why
http://frame:9000 yes a name with no dot in it cannot be a public domain
http://100.101.102.103:9000 yes picframe3 knows the tailnet range and allows it
http://frame.local:9000 yes mDNS, at home as always
http://frame.your-tailnet.ts.net:9000 no a registrable domain name — refused by design

So use the short name, which is nicer anyway. If you do want the full .ts.net name — for a bookmark that syncs across devices, say — add it under Settings → Control from elsewhere → advanced → Extra names this frame answers to, which is http.allowed_hosts in the config file:

http:
  allowed_hosts:
    - frame.your-tailnet.ts.net

Restart the frame afterwards.

Syncthing and the picture share over Tailscale

Both come along for free.

Syncthing's page is at http://frame:8384 and behaves exactly as it does at home — useful when you want to see why a folder has stopped syncing without standing in the room.

The picture share mounts too:

  • macOS: Finder → Go → Connect to Server → smb://frame
  • Windows: Explorer → \\frame\Pictures

It works, but SMB was designed on the assumption that the file server is in the same building. Browsing a folder is fine; copying four gigabytes of holiday photographs will test your patience. Syncthing is what you want for that.

The reason I actually did this: a frame at somebody else's house

This is the use case that makes the whole thing worth an afternoon.

You build a frame for your parents. You set it up at your kitchen table, you carry it over, you hang it on their wall, and it works. Six months later the phone rings and the frame is showing nothing.

Today, that call goes one of two ways. Either you talk a non-technical person through finding an IP address on a screen that is currently black, or you drive over.

With Tailscale on it, it goes like this instead:

ssh pi@parents-frame
picframe3 doctor

picframe3 doctor checks the hardware, the drivers, the permissions and the picture library, and prints the exact command that fixes whatever it found. If that is not enough:

journalctl -u picframe3@pi -n 50

If it needs an update, the same one-line installer that built it updates it in place over that same SSH session. And the web interface on http://parents-frame:9000 lets you see what is on their wall, skip a picture, change the interval and turn the screen off — from a phone, in another country.

Two practical notes.

Install it before the frame leaves your house. The same advice I gave about Dataplicity in 2019, and it has not aged: once the frame is on somebody else's wall, getting a terminal on it to install the thing that gives you a terminal is a chicken-and-egg problem with no elegant answer.

If you are posting a frame to somebody rather than carrying it, you can sign it in without a browser using a pre-authorised key. Generate one in the admin console under Settings → Keys, then:

sudo tailscale up --ssh --auth-key=tskey-auth-xxxxxxxxxxxx

Auth keys are valid for up to 90 days, so make it shortly before you need it, and treat it like a password while it exists.

Tell them it is there. You are putting software in somebody else's home that lets you log in whenever you like. Everyone I have done this for has been pleased — it is the difference between a frame that gets fixed and one that ends up in a drawer — but they should hear it from you first, and hear that it reaches the frame and nothing else in their house.

About that --ssh flag

sudo tailscale up --ssh turns on Tailscale SSH, which quietly removes a whole category of nuisance.

Normally, SSH means keys: generating them, copying them to the frame, keeping them somewhere, and rebuilding the lot when you change SD cards or get the "remote host identification has changed" warning. With Tailscale SSH on, the tunnel itself proves who you are, so ssh pi@frame from a signed-in machine simply lets you in. No key, no password.

You can turn it on later without redoing the setup:

sudo tailscale set --ssh

It listens on port 22 only, and it is governed by your tailnet's access rules — the default policy lets you reach your own devices, which is all a family tailnet needs.

One piece of advice: leave ordinary SSH working as well. Do not delete your keys, do not switch off sshd. If the tailnet itself ever goes wrong, ordinary SSH on the local network is the way back in, and it costs you nothing to keep.

A small bonus: sending a single photograph

Tailscale can move files between machines on the tailnet — a pleasant way to get one picture onto the frame without opening anything.

From your Mac or PC:

tailscale file cp ~/Desktop/christmas.jpg frame:

Then on the frame, to write whatever has arrived into the picture folder:

tailscale file get ~/Pictures

The frame notices new files within seconds and the photograph is on the wall.

It is a party trick rather than a workflow: for photographs arriving regularly, Syncthing or the file share are the right answers, and the frame is built around them. But for the one picture you want on your parents' wall this afternoon, it is rather nice.

Tailscale, Raspberry Pi Connect, or Dataplicity?

All three answer the same question — let me get to my frame from somewhere else — but not in the same way.

What you get On your own computer Cost
Raspberry Pi Connect A shell in a browser tab Nothing to install Free, personal tier
Dataplicity A shell in a browser tab Nothing to install Free on one device
Tailscale The whole frame — SSH, the web interface, Syncthing, the share The app, signed in Free, up to 6 users

Raspberry Pi Connect is first-party, out of beta, and needs nothing installed on the machine you are sitting at — which matters when that machine is somebody else's. If all you want is a terminal, use it. Its screen sharing does not work on a picframe3 frame, because there is no desktop session to share; I explain that in that article. The remote shell works perfectly.

Dataplicity still works and its free single-device tier is still there, but the company has moved on to selling fleet management to businesses, and the friendly hobby tool I wrote about in 2019 is now the evaluation tier of a commercial product.

Tailscale asks more of you — software on both ends, an account, one setting to change in an admin console — and gives you a great deal more in return. It is not the easiest of the three. It is the one I now have on every frame I look after.

And there is no reason to pick only one. Pi Connect and Tailscale sit on the same Pi perfectly happily, and having a second way in has saved me before.

When it does not work

The frame is offline in the admin console. Either it has no internet at all, in which case Tailscale is not your problem and my article on changing the Wi-Fi on a headless Pi is where to go, or its key expired — back to Step 2.

ssh pi@frame cannot resolve the name. MagicDNS is off, or Tailscale is not running on the machine you are sitting at. Check tailscale status on your own computer first; the IP address from tailscale ip -4 always works as a fallback.

The web interface refuses you with a message about the Host header. You used the long .ts.net name. Use http://frame:9000, or add the long name to http.allowed_hosts — see above.

Everything works at home and nothing works away. Check that Tailscale is actually switched on in the app on your phone. It is switched off more often than anyone admits.

In one sentence

Tailscale turns frame.local from my frame while I am at home into my frame, wherever I am — and it does it without opening a single port on anybody's router.