Skip to content
  • MangoFly

    A self-hosted WireGuard mesh. Devices connect straight to each other; the coordination server is one binary and a SQLite file, and never sees their traffic.

    encrypted WireGuard · peer to peerLaptopbehind home NATServerin a datacentrePhoneon mobile datacoordination serverone binary · one SQLite filecontrol plane only (TLS)keys · tunnel addresses · peer lists · sealed ICE candidatesholds no private keys · carries no traffic · cannot decryptdatacontrol
  • MangoDock

    Docker management with nothing on the hosts. Reaches each daemon over an ordinary SSH session — no agent to install, no port to open.

    The MangoDock dashboard showing three host cards with container state counts, CPU and memory gauges, a usage history and recent events
  • MangoWiFi

    A Wi-Fi 6/7/8 test bench. One binary runs as Console or Agent either side of the access point under test, measuring latency under real load.

    AP under testWi-Fi 6 / 6E / 7Agentstation side · real radioLAN receiveriperf3 -sConsoleUI · orchestrates · probes
  • Blog
  • Nothing phones home

    No telemetry, no analytics, no crash reporter, no account login. Check it with a packet capture on your own network.

    Download MangoSSH
  • Project
  • Download
  • Install & first run

    Set up a MangoFly server on a fresh Linux VM, one step at a time. About fifteen minutes, most of it waiting for DNS. Every command below is exactly what to type.

    What you need

    • A Linux VM with a public IP address — Ubuntu 22.04 or 24.04, or Debian 12, with at least 512 MB of RAM — 1 GB is comfortable. MangoFly itself uses only a few megabytes, but Docker, which runs it, needs around 150 MB on its own, so a 256 MB VM is too tight.
    • A domain name you can add DNS records to — Such as vpn.example.com. It has to be a name you control — not the VM's hostname, and not an IP address. This guide writes vpn.example.com; type your own wherever you see it.
    • An SSH session to the VM — Steps 1 to 7 and step 9 all run on the VM.
    • A second Linux machine with Docker — Any laptop or VM, for step 8 — enrolling it as your first device is how you prove the mesh works.

    1. Find the VM's public IP address

    Your cloud console shows it, or you can ask the VM. Write it down — step 2 needs it.

    curl -4 https://api.ipify.org

    2. Point your domain at the VM

    At your DNS provider, add an A record: the name is vpn (or whatever you chose), the value is the IP address from step 1. Then check it from the VM. When this prints the IP from step 1, move on. If it prints nothing, wait a few minutes and run it again — a new record can take a while to appear.

    getent hosts vpn.example.com

    Don't skip ahead. The installer asks Let's Encrypt for a certificate in this name, and Let's Encrypt can only issue one once the name points at this VM.

    3. Open the ports at your cloud provider

    Allow three ports in, from anywhere: TCP 80, TCP 443 and UDP 8788. This is your cloud provider's firewall or security group — a setting in the cloud console, not on the VM. TCP 80 has to be open before step 5, because that is where Let's Encrypt checks your domain.

    # GCP, from Cloud Shell:
    gcloud compute firewall-rules create mangofly \
      --allow tcp:80,tcp:443,udp:8788 --source-ranges 0.0.0.0/0
    
    # AWS:     EC2 > Security Groups > the VM's group > Edit inbound rules
    # Azure:   the VM's Network Security Group > Inbound security rules
    # Hetzner, DigitalOcean:  Firewalls > add the three rules

    UDP 8788 is the one people miss: the usual "allow HTTP and HTTPS" option covers TCP only. Without it everything still appears to work and devices enroll — but they cannot reach each other directly, and quietly fall back to the slower relay.

    4. Install Docker

    Docker's own install script sets up Docker Engine and the Compose plugin together. Then check both — each should print a version number.

    curl -fsSL https://get.docker.com | sudo sh
    
    sudo docker --version
    sudo docker compose version

    5. Run the MangoFly installer

    It asks one question, "Domain for this server". Type the domain from step 2 and press Enter. It then checks the machine, downloads three images and starts them — about a minute.

    curl -fsSL https://downloads.mangossh.com/mangofly/install.sh | sudo sh

    If your domain does not resolve yet, the installer warns you and carries on, but the certificate will fail until it does. That is fine to recover from: finish step 2, then run this same command again. Running it again is always safe.

    6. Save the password and the setup key

    When it finishes, the installer prints a summary like this. Copy the admin password and the setup key somewhere safe now — the password is shown once and is not saved anywhere you can read it later. The admin password is for the MangoFly desktop app, where you manage the mesh. The server address is not a web page to open in a browser; it is what you give the app and your devices.

    ================================================================
     MangoFly is installed.
    
     Server address  https://vpn.example.com
     Install dir     /opt/mangofly
    
     Admin created: admin
      password: (shown once - copy it now)
    
     Setup key     (a long random string)
     Enroll with   mangoflyd --server https://vpn.example.com --setup-key ...
    ================================================================

    The setup key never expires and has no use limit, so the same one enrolls every device. Treat it like a password: anyone who has it can add a device to your mesh.

    7. Check that it is running

    On the VM, all three services — mangofly, relay and caddy — should show as running. Then, from any computer, the health address should print ok. If that fails with a certificate error, the certificate has not been issued yet: go back over steps 2 and 3, then look at Caddy's log.

    cd /opt/mangofly && sudo docker compose ps
    
    curl https://vpn.example.com/health
    # prints: ok
    
    # if the certificate is the problem:
    cd /opt/mangofly && sudo docker compose logs caddy

    8. Enroll your first device

    On your second Linux machine, install Docker the same way as in step 4, then run MangoFly's client. Put in your own domain and the setup key from step 6. The volume keeps the device's identity, so it stays the same device when it restarts.

    sudo docker run -d --name mangoflyd --restart unless-stopped \
      --cap-add NET_ADMIN --device /dev/net/tun \
      -v mangoflyd-state:/var/lib/mangofly-daemon \
      -e MANGOFLY_SERVER=https://vpn.example.com \
      -e MANGOFLY_SETUP_KEY=paste-the-setup-key-here \
      -e MANGOFLY_DEVICE_NAME=my-first-device \
      ghcr.io/mangossh/mangofly-daemon:latest
    
    # within a few seconds this shows "connected to https://vpn.example.com":
    sudo docker logs mangoflyd

    9. Confirm it from the server

    Back on the VM, list the devices. Seeing my-first-device with an address like 100.64.0.2 means your mesh is working.

    cd /opt/mangofly && sudo docker compose exec mangofly mangofly-server --list-devices

    That is a complete, working server. To add more devices, repeat step 8 on each one with the same setup key and a different MANGOFLY_DEVICE_NAME.

    If something goes wrong

    • "run this as root" — It ran without sudo. Use the command from step 5 exactly as written, with sudo before sh.
    • "there is no terminal to ask for the domain on" — The installer could not show its question — usually because it ran from a script or automation rather than an SSH session. Give it the domain instead, written after sudo: curl -fsSL https://downloads.mangossh.com/mangofly/install.sh | sudo MANGOFLY_DOMAIN=vpn.example.com sh
    • "... does not resolve yet" — The A record from step 2 is missing, or has not appeared yet. Add or fix it, wait a few minutes, and run the installer again.
    • curl https://vpn.example.com/health fails — Almost always the certificate: the domain does not point at this VM, or TCP 80 is closed at the cloud firewall. Recheck steps 2 and 3, then read: cd /opt/mangofly && sudo docker compose logs caddy
    • The device never shows "connected to" — Check that the setup key was copied in full, and that MANGOFLY_SERVER is exactly your domain from step 2, starting with https://. The reason is in: sudo docker logs mangoflyd
    • Devices enroll, but are slow or cannot reach each other — Nearly always UDP 8788, closed at the cloud provider — see step 3. Everything else works without it, which is what makes it easy to miss.

    Looking after it

    Everything the install owns lives in /opt/mangofly. To upgrade, run the installer again: it asks for the domain once more (type the same one), keeps your admin account and relay secret, and starts the newest images.

    cd /opt/mangofly && sudo docker compose ps        # what is running
    cd /opt/mangofly && sudo docker compose logs -f    # follow the logs
    
    # upgrade:
    curl -fsSL https://downloads.mangossh.com/mangofly/install.sh | sudo sh

    What the installer does

    Worth knowing if you want to read it before running it, or set things up by hand. It checks for root, Docker and the Compose plugin; resolves your domain and warns if it does not point at this VM; writes a .env, a Caddyfile and a docker-compose.yml into /opt/mangofly, with a generated secret for the relay; pulls the coordination server, the relay and Caddy and starts them; waits for the server to report healthy; then creates the admin account and a setup key. Nothing is compiled on the VM, and nothing is cloned — it writes the three files itself.

    # to read it before running it:
    curl -fsSL https://downloads.mangossh.com/mangofly/install.sh -o install.sh
    less install.sh
    sudo sh install.sh

    Settings you can change later

    These live in /opt/mangofly/.env. After editing, apply them with: cd /opt/mangofly && sudo docker compose up -d

    • MANGOFLY_ADMIN_ALLOW_FROM — Empty by default, which means the admin API and metrics answer from anywhere. Set it to your mesh range so they answer only over the mesh.
    • MANGOFLY_VERSION — latest by default. Set a version number to stay on exactly that version.
    • MANGOFLY_REGISTRY — Where the images come from. Point it at an internal mirror if the VM has no route to the public registry.