Self-hosted service
bitxeno/atvloadly avatar
bitxeno/atvloadly

atvloadly signs IPAs from a headless Linux container

Easily sideload the IPA to AppleTV

1,596 stars93 forksGoAGPL-3.0

At a glance

What is it?
atvloadly is a Go web service that installs iPad and iPhone applications on an Apple TV by driving the Impactor signing flow over a headless connection, then keeps them refreshed so they do not expire. It only runs on Linux or OpenWrt, it holds Apple ID credentials, and it has no authentication of its own.
Who is it for?
Adopt atvloadly if you own the apps you are installing, run a Linux or OpenWrt host with avahi available, and accept storing an Apple ID in a container that has no login. Do not expose the web interface beyond a trusted network, since the README states there is no authentication.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A web UI in front of Impactor

The README describes atvloadly as a web service that supports sideloading apps on Apple TV, using Impactor as the underlying technology for sideloading and automatically refreshing the app so it stays available long term. That last clause is the point of the whole project: free-Apple-ID signing produces certificates that expire, and something has to re-sign on a schedule or the installed app stops launching.

Around that core it adds the pieces a home server needs. Pairing with the Apple TV is handled through the UI, automatic app refresh is on by default, and the service tracks GitHub releases and AltStore sources so an app can update manually or automatically. You can register multiple Apple ID accounts, and the README also documents an external certificate path where a `.p12` plus a tvOS `.mobileprovision` profile replaces the Apple ID entirely.

The interface is multilingual, with English, Swedish and Chinese READMEs and a `locales/` directory at the root of the repository.

One feature deserves separate mention because it changes what you can install: the app can be installed under a custom bundle identifier, next to the original app, using either an Apple ID or a P12 certificate.

Linux or OpenWrt, and nothing else

The platform constraint is stated as a warning above the install instructions: only Linux and OpenWrt are supported, with no Mac or Windows support. That is a direct consequence of how discovery works rather than a porting choice.

Finding an Apple TV on the network requires mDNS, which on Linux means avahi. The README's first step is installing the daemon, and the OpenWrt and Ubuntu routes differ:

bash
opkg install avahi-dbus-daemon
/etc/init.d/avahi-daemon start
bash
sudo apt-get -y install avahi-daemon
sudo systemctl restart avahi-daemon

The container needs the host's dbus and avahi sockets mounted in, and it runs with seccomp unconfined because the sideload process makes syscalls the default profile blocks. The Docker Compose file in the repository spells the whole shape out: image `bitxeno/atvloadly:latest`, the socket mounts, a port mapping of 5533 to 80, and `restart: unless-stopped`.

If discovery still fails, the README's FAQ suggests trying `privileged: true` in compose, or `--privileged` in `docker run`, in place of `--security-opt seccomp:unconfined`. That is a broader grant than the default, and it is offered as a fallback for people whose setup cannot work otherwise.

The IPv4 versus IPv6 detail in the FAQ is worth knowing too: a device is found when the tool can see `_remotepairing-manual-pairing._tcp` entries, so a VPN is a common cause of a network that appears empty.

Running it with Docker

The Docker invocation in the README is long enough to be worth reading rather than pasting blind:

bash
docker run --security-opt seccomp:unconfined -d --name=atvloadly --restart=always -p 5533:80 -v /path/to/mount/dir:/data -v /var/run/dbus:/var/run/dbus -v /var/run/avahi-daemon:/var/run/avahi-daemon bitxeno/atvloadly:latest

The `-v` for the mount directory becomes `/data` inside the container, which is where the database and the signing key live. The other two mounts share the host's dbus and avahi sockets so mDNS discovery works from inside.

The compose route downloads the file the project publishes:

bash
wget https://raw.githubusercontent.com/bitxeno/atvloadly/refs/heads/master/docker-compose.yml
docker compose pull
docker compose up -d

If you want host networking and a different port, the README gives a single environment variable:

bash
SERVICE_PORT=5533

From there the pairing flow is entirely in the browser. On the Apple TV, Remote and Devices, then Remote App and Devices, then pairing mode. The web page lists the pairable device, you click it to pair, and the paired Apple TV appears on the home page where you select an IPA and install.

Two signing paths with different expiry rules

This is where the design has consequences, and the README is unusually explicit about both.

With a free Apple ID, the constraints are Apple platform constraints rather than atvloadly ones. Each free Apple ID can register up to 10 apps and activate up to 3 simultaneously, and installing more than 3 makes previously installed apps unavailable. Certificates are shared across every sideloading tool that uses the account, so when capacity runs out atvloadly will not silently revoke anything: the install page asks which certificate, if any, may be revoked. The README also advises using a dedicated account for installation and creating a separate one rather than reusing an account you care about.

With an external certificate, you import a `.p12` with its password, which may be empty, and a matching `.mobileprovision` profile that must be a tvOS profile listing the Apple TV UDID. No Apple ID is asked for or used at all. The trade is refresh: apps signed this way are not refreshed automatically, they expire at the earlier of the certificate and profile expiries, and reinstalling with the same certificate does not extend that date. You have to replace the profile or the identity and then reinstall.

The private keys are stored encrypted with a deployment key file, `signing.key_file`, defaulting to `/data/keys/signing-identity.key`. The README's instruction is to back that file up together with the database, which is the whole persistence story in one sentence.

No authentication, and Apple credentials in the container

The README says it directly: atvloadly has no authentication, so keep it on a trusted network. That is the single most important sentence for anyone deploying this.

The web interface is the control surface for paired devices, stored Apple ID credentials and installed apps. Publishing port 5533 to the internet, or putting it on a shared VLAN, hands all of that to anyone who can reach the page. There is no login form to bypass because there is no login at all.

The Apple ID side has its own exposure. When you sign in through the interface, atvloadly needs to be authorised as a trusted device, and the README notes it is virtualised as a MacBook in that process. Apple then sends a two-factor code to the registered phone number or to a device already logged in with that account, and the README asks you to authorise promptly. That is the mechanism, and it is also why a dedicated install account is the sensible choice: the trust grant and the two-factor destination are attached to that account.

Apple ID credentials and the private keys for external certificates therefore live in the same container, and the container is a web server. Treat the `/data` mount as secret material.

For what it is worth on the build side, the Dockerfile is on `ubuntu:22.04`, installs `libavahi-compat-libdnssd-dev`, unpacks per-architecture binaries for amd64 and arm64, places a signing helper at `/usr/bin/plumesign`, and pulls one additional native library out of an Android APK at build time.

Failure modes and the release line

The FAQ section answers the questions people actually hit.

If a device cannot be found, turn off the VPN, restart the Apple TV and re-enter pairing mode. If login fails, the README says Apple's risk control may have triggered, that some regions have login restrictions, and that a proxy in the settings or a new account are the suggested workarounds. App-specific passwords are not supported, which rules out one obvious hardening step. If an IPA crashes after install, the cause is usually the bundle identifier rewrite atvloadly applies, or a capability such as CloudKit that only a paid developer account can sign. After a tvOS upgrade the device must be re-paired, and the README recommends disabling automatic system updates because new releases are often unsupported. There is a `/healthcheck` API endpoint for monitoring.

On versions: the recent tags are v0.6.4, v0.7.0 and v0.7.1, all labelled tvOS 17.4+, dated 2026-10-12, 2026-10-14 and 2026-10-14. So the project ships tvOS-version-targeted releases, which means the version string is the compatibility statement. The last push was on 2026-10-15.

The module is `github.com/bitxeno/atvloadly` on Go 1.25, and the dependency list shows the shape of the thing: Fiber for the web layer, GORM with SQLite for storage, zerolog and lumberjack for logs, cron for scheduled refresh, go-i18n for locales, notify and a WeChat client for alerts, plist and pkcs12 for profiles and certificates, and zeroconf with go-avahi for discovery. There is also a Model Context Protocol Go SDK dependency, so the service exposes an MCP interface in addition to its web UI.

Editorial conclusion

Adopt atvloadly if you own the apps you are installing, run a Linux or OpenWrt host with avahi available, and accept storing an Apple ID in a container that has no login. Do not expose the web interface beyond a trusted network, since the README states there is no authentication. Verify first that your Apple TV is on a tvOS version the release covers, v0.7.1 targets tvOS 17.4+, that your free Apple ID still has certificate capacity, and that you have backed up the signing key file at /data/keys/signing-identity.key together with the database.

Frequently asked questions

How do I install atvloadly?

On a Linux or OpenWrt host, install avahi-daemon, then run the container mounting the host's dbus and avahi sockets plus a data directory, for example `docker run --security-opt seccomp:unconfined -d -p 5533:80 -v /path/to/mount/dir:/data -v /var/run/dbus:/var/run/dbus -v /var/run/avahi-daemon:/var/run/avahi-daemon bitxeno/atvloadly:latest`. A compose file is published on the master branch.

Can atvloadly run on macOS or Windows?

No. The README states it only supports Linux and OpenWrt systems and does not support Mac or Windows, because device discovery relies on avahi mDNS.

How many apps can I install with a free Apple ID?

Each free Apple ID can register up to 10 apps and activate up to 3 simultaneously. Installing more than 3 causes previously installed apps to become unavailable.

Does atvloadly support signing with my own certificate?

Yes. You can import a .p12 and a matching tvOS .mobileprovision profile and choose External certificate on the install page, with no Apple ID used. Apps signed that way are not refreshed automatically and expire at the earlier of the certificate and profile expiries.

Does atvloadly have a login screen?

No. The README states atvloadly has no authentication and should be kept on a trusted network, so the web interface and the stored Apple ID credentials are reachable by anyone who can reach the port.

Official sources

  1. bitxeno/atvloadly on GitHub
  2. Issues
  3. License: AGPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bitxeno-atvloadly.svg)](https://hysenlabs.com/projects/bitxeno-atvloadly)

Was this useful?