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:
- Sign up at cloudflare.com, Add a site, enter your domain, choose the free plan.
- 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.
- Networks → Tunnels & Mesh → Create a tunnel, name it, and select Create Tunnel.
-
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. -
Give the token to
manuserver tunnel adminfirst, 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 typeHTTP, URLlocalhost:80— port 80, not the 8080 in the placeholder. Cloudflare writes the DNS record itself.
The bare domain does not coverwww.— if you want that too, add a second route withwwwin 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.