Stand up a complete network on one machine: coordinator, TLS, database, and profile hosting.
This guide stands up a complete network on one Ubuntu server: coordinator, database, TLS, and profile hosting. Every step has a matching script in the API repository's deploy/ directory; the scripts are short and idempotent, so you can run them or read them and follow along by hand. Read the Self-Hosting Overview first if you haven't.
Throughout, replace yourdomain.site with your domain and you with your deploy username.
Create two A records pointing at your server:
yourdomain.site
*.yourdomain.site
The wildcard serves every node subdomain (alice.yourdomain.site); www rides the wildcard too.
Connect as root, update, then create your deploy user and firewall:
ssh root@yourdomain.site
apt update && apt upgrade -y && reboot
# After the reboot, back in as root: clone the API repo straight to its
# home and run the setup script from it.
apt install -y git
git clone https://codeberg.org/HuntrSoftware/nodeyou-api.git /opt/nodeyou-api
DEPLOY_USER=you /opt/nodeyou-api/deploy/setup-server.sh
The script creates the user (sudo group, your SSH key copied over) and enables ufw with SSH allowed. Once you confirm you can ssh you@yourdomain.site, disable root login: set PermitRootLogin no in /etc/ssh/sshd_config and sudo systemctl restart ssh. Everything below runs as the deploy user.
The clone from step 2 is the deployment itself; it just still belongs to root. Give it to your user, then work from its deploy/ directory for the next steps:
sudo chown -R you:you /opt/nodeyou-api
cd /opt/nodeyou-api/deploy
/opt/nodeyou-apiis convention, not requirement: the scripts run relative to their checkout, andsetup-api.shrenders the systemd unit against wherever the checkout lives. Just keep it somewhere your user can reach (not under/root).
DOMAIN=yourdomain.site ./setup-nginx.sh
On first run this installs nginx and stops: the site config references a wildcard certificate that does not exist yet. Issue it with the printed command:
sudo apt-get install -y certbot
sudo certbot certonly --manual --preferred-challenges dns \
-d "yourdomain.site" -d "*.yourdomain.site"
Wildcard certificates require the DNS-01 challenge: certbot prompts you to add one or two _acme-challenge TXT records at your DNS provider. Then re-run setup-nginx.sh; it detects the certificate lineage (including -0001 style suffixes), renders the site config from deploy/nginx-site.conf.template, and reloads nginx.
A manually issued DNS-01 certificate does not renew unattended. Either repeat the
certonlyflow before expiry, or install your DNS provider's certbot plugin socertbot renewcan automate it.
./setup-postgres.sh
Installs PostgreSQL and creates the nodeyou database and role. Record the generated password it prints; it goes into .env next and is not stored anywhere else.
.env.example documents every setting the API reads:
cd /opt/nodeyou-api
cp .env.example .env
nano .env
At minimum set:
DOMAIN, and CERT_NAME if certbot suffixed your lineage (step 4)DATABASE_URL with the password from step 5INTERNAL_SECRET, shared with every service agent (generate one: python3 -c "import secrets; print(secrets.token_hex(32))")SECRET_KEY for JWT signing (generate the same way)KEY_ENCRYPTION_KEYS (the generation command is in the file's comment)ADMINS: your email. Accounts with these emails get the in-app Admin pane and bypass registration gating, which is how the first account on a gated deployment gets created.REGISTRATION_MODE and DEFAULT_SERVICE_ALLOWANCE: see Network Administration. invite mode with a default allowance of 0 is a sensible private-network start.Leave the Stripe and Google Play sections commented out; the network runs unmetered without them. Then:
./deploy/setup-api.sh
This builds the venv, installs dependencies, runs migrations, and installs + starts the nodeyou-api systemd service. Check it: https://yourdomain.site/api/docs should show the Swagger UI.
Profile services run in containers, so install Docker Engine per the official instructions, then let your user run it:
sudo usermod -aG docker you
newgrp docker
docker run hello-world
The profile image is built locally on every server that hosts profiles; there is no registry to pull from:
sudo mkdir -p /opt/ny-profile-api && sudo chown you:you /opt/ny-profile-api
git clone https://codeberg.org/HuntrSoftware/ny-profile-api.git /opt/ny-profile-api
cd /opt/ny-profile-api
docker build -t nodeyou/profile-api:latest .
Rebuild with the same command whenever you update the checkout; then restart profile containers to pick it up.
Even a single-server network runs the service agent: the API delegates all container lifecycle to it, on this box over loopback. A fresh database already contains the matching server row (main, 127.0.0.1, agent port 8001).
sudo mkdir -p /opt/ny-service-agent && sudo chown you:you /opt/ny-service-agent
git clone https://codeberg.org/HuntrSoftware/ny-service-agent.git /opt/ny-service-agent
cd /opt/ny-service-agent
cp .env.example .env
nano .env
Set:
WG_ADDRESS=127.0.0.1
INTERNAL_SECRET=<same value as the coordinator's .env>
SERVER_NAME=main
DOMAIN=yourdomain.site
NODE_API_URL=https://yourdomain.site
Edit docker-compose.yml: name the service and container (e.g. main-agent), and because the API already owns port 8000 on this host, change the ports line to publish the agent on 8001:
ports:
- "127.0.0.1:8001:8000"
Prepare the host and start the agent:
cd /opt/nodeyou-api && ./deploy/prepare-profiles.sh
cd /opt/ny-service-agent && docker compose up -d --build
curl http://127.0.0.1:8001/api/internal/health
prepare-profiles.sh creates the profile data directories and the nodeyou Docker network, and grants the API what it needs to manage per-profile nginx configs: the sudoers rule for nginx -t + reload, and write access to /etc/nginx/sites-enabled (where it writes each profile's config on provision). Its final output verifies all of this; every line should show ✓.
Point a client at your network: open the app, enter https://yourdomain.site in the Host Provider field on the Start screen, and create the account whose email you put in ADMINS. You land on your dashboard with an Admin tab in Settings.
To prove the whole pipeline, launch a profile on yourself: in the Admin pane, set your own account's allowance to 1 (Network Administration covers this surface). Create a node, then Services > Launch a new service > Profile; the dialog shows Activate because your account holds an allowance. A few seconds later https://<yournode>.yourdomain.site serves your profile.
The site root (/var/www/yourdomain.site) serves whatever you put in it; the API and node subdomains work regardless. To offer a browser client on your own domain, build the app for web (see its repository for the WASM build) and copy its dist/ there. Otherwise, users install the desktop or Android app and point it at your host.
To update the API: pull the checkout, then re-run ./deploy/setup-api.sh (it re-installs dependencies, migrates, and restarts the service). To update the profile image: rebuild it (step 8) and re-provision or restart profile containers. To update the agent: pull and docker compose up -d --build.