Many apps are open to the first person who reaches them, and some ship with a known login. This guide shows how to close that gap, which apps should stay private, and how to send email without running a mail server.
The gap between install and setup
You start the container. The app runs, but you have not configured it yet. In that window, many apps let the first visitor create the admin account. A few ship a known invitation code or password. If the app is reachable from the internet, the first visitor may be a scanner, not you. Why you should not open ports explains how fast that happens.
Docker adds a trap. A published port is available to the outside world by default, and if you use ufw or firewalld, published ports bypass your firewall rules. A firewall rule you set up may do nothing for a container port.
The rule is simple: finish setup before anyone else can reach the app. Do it in one of two ways.
- Publish the port on the loopback address only. In your Compose file, write the port like this:
ports:
- "127.0.0.1:3000:3000"
With this form, only the Docker host can reach the port. On a remote server, open an SSH tunnel from your laptop, then browse to http://localhost:3000:
ssh -L 3000:127.0.0.1:3000 you@your-server
- Reach the app over a private network such as Tailscale. What is Tailscale? covers it.
Create your accounts this way. Decide about a public address afterwards, using Choose your front door.
What the apps do on first visit
Check each app in its own docs. Behaviour changes between versions. These notes are current as of October 2026.
Uptime Kuma. Version 2 first asks which database to use, then shows a "Create your admin account" screen until a user exists. Whoever opens the page first picks the username and password. See Uptime Kuma.
Gogs. The latest release, 0.14.3, shows an install page on first run (INSTALL_LOCK = false until setup is done). Whoever submits that page sets up the server. Fill in the optional admin account section there, and tick "Disable Self-registration" on the same page. Newer development builds drop the install page. Their docs say whoever signs up while there are no other users becomes the admin, or you create the admin with gogs admin create-user. Either way, the first visitor wins, so do it over a private address. See Gogs.
Huginn. The official Docker docs now say you log in as admin with a random password printed once in the container log. That is a recent change. Until September 2026, seeding used a fixed, documented password, so an older image or tutorial may still tell you to log in with a known one. Read the log, or set SEED_USERNAME and SEED_PASSWORD yourself. Huginn also has a sign-up invitation code. Its example config sets INVITATION_CODE=try-huginn with the comment "PLEASE CHANGE THIS", and the code falls back to try-huginn when the variable is unset. See Huginn.
AnythingLLM. In the Docker version, an instance has no password by default. Anything that can reach it can use it and change its settings. Turn on "Password Protect Instance" for one person. Turn on multi-user mode if others need their own logins, but note you cannot go back to single-user mode. See AnythingLLM.
LobeChat (now LobeHub). Older guides tell you to set an ACCESS_CODE. LobeHub 2.0 removed that variable, and setting it can fail the deployment. Use its login system instead. Registration is open to any email unless you set AUTH_ALLOWED_EMAILS to your own address or domain. That setting does not check that the person owns the address. The same page says to add AUTH_EMAIL_VERIFICATION=1, which needs working email (see below). Until then, register your own address first, over a private address. See LobeChat.
Turn off open sign-up
After you create your accounts, close the door behind you. A public sign-up form lets strangers store data, use your storage and, on some apps, use your paid API keys.
In Gogs, edit app.ini. For a binary install it is custom/conf/app.ini. For the Docker image it is /data/gogs/conf/app.ini inside the container, which is gogs/conf/app.ini in the host folder you mounted at /data. In the [auth] section, set DISABLE_REGISTRATION:
[auth]
DISABLE_REGISTRATION = true
Restart Gogs. With registration off, accounts must be created by admins. The same file has REQUIRE_SIGNIN_VIEW, which hides the site from anonymous visitors. In the 0.14.3 file, registration is off only if you turn it off.
In LobeHub, use AUTH_ALLOWED_EMAILS. In Huginn, change INVITATION_CODE to a long random string. Other apps name this setting differently. Search the app's docs for "registration" or "sign up".
Which apps stay private
Some apps cost you money or give a stranger control of a machine. Keep these on a private network.
| App | Where to run it | Why |
|---|---|---|
| Collie | Private network only | It gives remote shell access to your machine by design. Treat its URL as a root login. Never use Tailscale Funnel. |
| Huginn | Private | Agents make web requests and run actions for you. Its ShellCommandAgent runs commands when you allow insecure agents. Leave that off. |
| AnythingLLM | Private | It holds your model API keys. A stranger who uses it spends your money. |
| LobeChat | Private, or login only | Same key risk. If you share it, restrict sign-up first. |
| GoAccess | Private | Its reports come from access logs, which list visitor addresses and the pages they read. |
| Gogs | Public is possible | Only with registration off and strong passwords. Keep it patched. |
If you are unsure, start private. Moving an app to a public address later is easy. Cleaning up after a breach is not. For AI apps, Self-hosted AI: local or API covers the cost side.
Email: password resets and invites
Many apps send mail for password resets, invites and verification. LobeHub, for example, needs an SMTP server or Resend for those. Huginn reads its SMTP settings from environment variables. SMTP is the protocol apps use to send mail.
Without working email, a forgotten password can mean editing a database. Set it up before you invite anyone.
Why you cannot send from your own server
Many networks block the classic mail port, 25.
- Comcast does not support port 25 for residential customers and recommends port 587.
- Hetzner blocks ports 25 and 465 by default on cloud servers. Port 587 is open.
- DigitalOcean blocks ports 25, 465 and 587 on Droplets and advises against running your own mail server.
- Google Cloud blocks port 25 to outside destinations. Amazon EC2 throttles port 25 by default.
Even with the port open, mail from a new server often lands in spam. Receivers want proof that the sender is allowed to use your domain.
Use a transactional relay
A transactional relay is a service that sends mail for you through its own trusted servers. Your app connects to it over SMTP. The default for a novice is a relay, not your own mail server.
Resend and Amazon SES are two examples. Both offer alternate ports such as 2587 and 2465, which helps on a host that blocks 587. Compare current prices on their own pages, because they change.
Set it up in this order:
- Create an account with the relay and add your sending domain. Resend requires a verified domain.
- Add the DNS records the relay shows you. They include SPF and DKIM. SPF lists the servers allowed to send for your domain. DKIM adds a signature that receivers check against a key in your DNS. Google asks all senders to set up SPF or DKIM, and bulk senders to set up both plus DMARC.
- Put the relay's host, port, user and password in the app's SMTP settings.
- Send a test reset mail to an address you own. Check the spam folder.
Pre-share checklist
- Setup was done over localhost, SSH tunnel or Tailscale.
- The admin account has a long, unique password. No default login remains.
- Open sign-up is off, or limited to your email or domain.
- Any invitation code or access code is changed or removed.
- Apps that hold API keys, or can run commands, stay private.
- Port publishing uses
127.0.0.1unless you chose a front door on purpose. - A test password-reset email arrives.
Next: Backups you can restore.