MANUSERVER

A bootable Linux server that hosts (locally or in a virtual machine) a website + its PostgreSQL database.

Note: this is the quickstart. For more info, visit the repo.

At the moment the server is installed with tastehopping.com: an anonymous place for YouTube videos people around the world want to expose, and for voting on them.

In tastehopping.com, there are no emails, no passwords and no profiles — joining is one button, and the server hands you a made-up name and one long key that is the account. Keep it however suits you: mail it to yourself, or write it on paper. This is a small, disposable service, and a lost key costs you an account and nothing more. If one does go missing, you are welcome to make a new account whenever you need one. Accounts that go three months without being used are deleted along with what they saved, so simply visiting keeps yours alive.

To build the .iso: Arch Linux on an Intel or AMD machine, and 15 GB free.

How to use in Virtual Machine

When you are doing the step ./manuserver.sh vm_install, give the server a different username from the one on your own computer. These commands run on your machine but reach across to the server, and the password they ask for partway through is the server's. With the same name on both, the prompt looks identical whichever one is asking, and the obvious guess is the wrong one.

git clone https://github.com/manujarvinen/manuserver.git
cd manuserver
./manuserver.sh build_iso     # 5-20 min
./manuserver.sh vm_install

Four questions — network, username, password, which disk — then it sets up nginx, PHP, Postgres and the database itself. Afterwards it offers you a manuserver command, and to delete the .iso and the clone. Neither is needed to run the server.

How to use in 64-bit PC

When the installer asks you the four questions, give the server a different username from the one on your own computer. You will nearly always be reaching this machine over ssh from another one, and once you are in, every prompt — sudo, a database dump, a restore — belongs to the server. With the same name on both, nothing on screen tells you which machine you are typing at. Different names and the shell prompt itself answers it.

Build the .iso as above, then write it to a whole USB disk — that disk is completely overwritten. Caligula lists the disks and you pick one, so there is no device path to mistype. balenaEtcher does the same on Mac or Windows.

sudo pacman -S caligula
caligula burn -z none manuserver-*.iso

Boot the PC from the stick and answer the same four questions. It has to start in UEFI mode with Secure Boot off — both are firmware settings.

Running it in Virtual Machine

manuserver               start it
manuserver stop          shut it down
manuserver status        is it running?
manuserver ssh admin     open a terminal on it
manuserver tunnel admin  put it on the public internet
manuserver backup admin  save the database to a file
manuserver restore       put the newest saved one back

No window opens; it runs in the background, the way a server does. The site is at localhost:8080.

The admin in those lines is the username on the server, and so is every password they ask for — you are at your own computer, but nothing here is asking about it. This reads much more clearly when the two machines are not both called the same thing.

Leave the name out and it uses yours, from this computer — which, if you took the advice above, is an account the server does not have. Then it asks for a password no answer can satisfy, the right one included. Any password these commands ask for belongs to the server: first its ssh login, then sudo once you are in, both typed into your own terminal and neither of them labelled.

Running it in 64-bit PC

Nothing to start. It logs itself in and brings its services up at boot, so powering it on is the whole of it.

Its own screen shows the address it has on your network — that is where the site is, and where to ssh.

ssh admin@192.168.1.50         open a terminal on it
sudo manuserver-tunnel         put it on the public internet
sudo manuserver-tunnel status  is the tunnel up?
sudo manuserver-tunnel off     take it back off

cd ~
sudo -u postgres pg_dumpall --clean > backup.sql   save the database
sudo -u postgres psql < backup.sql                  put it back

admin and the password are the ones you gave the installer, not your own computer's — which is far easier to keep straight if you did not give the two machines the same username. Copy that backup file off the machine and you cannot lose the database. Putting one back replaces everything currently in it.

The manuserver command drives a virtual machine from outside, so it is not on this machine. Everything here you do over ssh.

Saving the database, and putting it back

The database is the only thing on the machine that cannot be rebuilt. Everything else — the system, the site, the tunnel — comes back from a reinstall. The saved videos and the accounts do not.

With the server in a virtual machine, both directions are one command, run on your own computer:

manuserver backup admin    save it to ~/Downloads
manuserver restore admin   put the newest saved one back

The file lands in your Downloads folder named by date, like manuserver-2026-07-28-2101.sql, and it is the whole database as plain text. restore takes the newest manuserver-*.sql there and ignores any other .sql you happen to have; to choose a different one, name it: manuserver restore ~/Downloads/manuserver-2026-07-28-2101.sql. One word that is not a file it can find is read as a username instead, so restore admin does what it looks like.

Both need the server running, and both ask for the server's password twice — once for ssh, once for sudo at the far end. Restoring replaces what is on the server, so it asks you to confirm first.

On a real computer there is no manuserver command to do it for you, so it is Postgres directly, in an ssh session on the machine:

cd ~
sudo -u postgres pg_dumpall --clean > backup.sql   save the database
sudo -u postgres psql < backup.sql                  put it back

Your home folder is the right place for it, and both lines are written so it works from there. < backup.sql, not -f backup.sql — the < is your shell opening the file as you and handing it over already open, where -f makes psql open it itself, as postgres, which cannot see inside your home folder. That one says Permission denied about a file you are looking straight at, and reads like a broken backup rather than a folder it was never allowed to enter.

Nothing asks you to confirm that one, and it replaces everything currently in the database. Copy the file off the machine afterwards — a backup sitting on the disk it is a backup of is not one. A USB stick, at the machine's own keyboard:

lsblk -f                            find the stick
sudo mount /dev/sdb1 /mnt
cp backup.sql /mnt/
sudo umount /mnt                    before pulling it out

lsblk -f lists the disks; the stick is the one that appeared when you plugged it in, and /dev/sdb1 is a guess at its name rather than a fact. Unmount before unplugging or the copy may still be in memory and not on the stick. From another computer it is scp admin@the-server:backup.sql . instead, and on a desktop with the stick already mounted for you, dragging the file across and pressing eject is the same thing.

A backup is readable text and holds every saved video and every account name, so treat it as private. It does not let anyone log in as those accounts: only the SHA-256 of each key is stored, never the key. Nor does it carry the tunnel token, which lives in /etc/manuserver/ and not in the database. Take one before vm_install, which wipes the virtual machine and the database with it.

On the internet

A Cloudflare tunnel publishes it without opening anything on your router. Already installed, and off until you give it a token.

Getting one is free, in a browser, once:

  1. Sign up at cloudflare.com, Add a site, enter your domain, choose the free plan.
  2. Point your registrar at the two nameservers it gives you and wait for Active — minutes to a day, and the only slow part. This one is not optional: the record that points a hostname at a tunnel only resolves inside Cloudflare's own DNS, so it cannot be added at your registrar or in cPanel.
  3. Networks → Tunnels & Mesh → Create a tunnel, name it, and select Create Tunnel.
  4. It offers an install command. You do not run it — the long eyJhIjoi… string inside it is the token, and that is all you need. Treat it like a password.
  5. Give the token to manuserver tunnel admin first, so the tunnel is connected. Then Networks → Tunnels & Mesh, click your tunnel, and open Published application routes. Leave Subdomain and Path empty, pick your domain, and set the service to type HTTP, URL localhost:80 — port 80, not the 8080 in the placeholder. Cloudflare writes the DNS record itself.
    The bare domain does not cover www. — if you want that too, add a second route with www in the Subdomain box.
    Not Networks → Routes in the side menu; that one is for private networks and cannot publish a site.

If saving the route is refused with a DNS record with this name already exists, the domain already had one at the root — Cloudflare imported it when you added the site. Delete that A record under DNS → Records in the main dashboard, leave the MX ones alone or the domain stops receiving mail, and save again.

One command asks for the token, and it runs on the server. Which one you type depends only on where you are sitting: at your own computer with the server in a virtual machine, manuserver tunnel admin; at the server's own keyboard, or in an ssh session on it, sudo manuserver-tunnel. Nothing appears as you paste, which is deliberate.

At a real server's own keyboard there is nothing to paste from — no browser, no clipboard, and the token is far too long to type off a phone screen. Bring it on a USB stick instead; manuserver-tunnel reads it from standard input, so a file works with no change to anything:

lsblk -f                            find the stick
sudo mount /dev/sdb1 /mnt
sudo manuserver-tunnel < /mnt/token.txt
sudo umount /mnt

Whole install command or just the token, wrapped across lines or not — the spaces and newlines are collapsed and the token taken out of it, the same as at the prompt. Then delete the file and take the stick away: this is the one secret on the machine, and a copy of it in a drawer is a copy of it. It is also the one thing you can always throw away and replace — sudo manuserver-tunnel off forgets it and Cloudflare will issue another.

Healthy is not the same as reachable. It only means the tunnel connected. There is no public IP to visit — a tunnel is reached by hostname and nothing else — so until a route exists in an Active zone, there is no address at all. To try it before the DNS move lands, run cloudflared tunnel --url http://localhost:80 on the server for a throwaway trycloudflare.com address.

Security

Root is locked, and the virtual machine is bound to your own computer. There is no database password, because a password has no use — nothing reaches the database from the network, and only the website and the sudo user can open it. And a stored secret could be stolen.

It does not protect you from: whoever holds an account key is that account, Cloudflare can see the traffic it carries, and the disk is not encrypted.

The one secret on the machine is the tunnel token, in /etc/manuserver/tunnel.env. It never reaches this repo, your shell history or a database backup — but it is part of the disk, so a copy of the whole disk or of the virtual machine's image carries it too. On a real computer, SSH also accepts a password until you copy a key over.