The Noodle server keeps your devices in step: the Mac app, your phone and any browser all see the same pages, updated as you type. The same address also serves Noodle in the browser, the server backs itself up, and if you like it can connect AI assistants such as Claude. It runs in Docker on a machine you own, so your data stays there and never passes through an outside service.
You need an always-on machine with Docker and Docker Compose: a mini PC, a NAS that supports Docker, a virtual machine at home or a rented server (VPS). 1 GB of RAM and a few GB of disk are enough, plus room for attachments and backups.
The Noodle app's interface is in Italian for now, so this guide quotes its labels in Italian with an English translation alongside.
01Before you start
What you need
- An always-on machine with Docker and Docker Compose v2 (the command is
docker compose, with a space). If Docker isn't installed, follow the official instructions for your system. - The server package: the
noodle-server-<version>.zipfile on the Download page. It contains anoodle-server/folder withserver/,apps/web/,shared/,deploy/and anISTRUZIONI.mdfile, written for an AI assistant but readable by anyone (it is in Italian; an assistant will handle it fine). The Mac app can save the same package: on first launch, choose "Il mio server" (My server), then "Devo installarlo" (I need to install it), then "Salva il pacchetto..." (Save the package). - Optional but recommended: an HTTPS address, through a domain or through Tailscale. Noodle works without it on your local network, but in the browser passkeys (fingerprint and face unlock) and push notifications need HTTPS.
Five questions to ask yourself
- Where will the server run? A computer at home, a NAS or a VPS. Is Docker already installed?
- Where will you use it from? Only at home, away from home through Tailscale or a VPN, or from the internet with a domain.
- Do you already have an HTTPS proxy (Nginx Proxy Manager, Caddy, Traefik) or a domain?
- Do you want to connect AI assistants such as Claude or Codex? You can decide later.
- Where will backups be copied off the machine: a NAS, an external drive, a cloud service?
The answers only affect sections 6, 7 and 8. The basic installation is the same for everyone.
02Two ways to install it
You can have an AI assistant working in your terminal guide you through it, or follow this page by hand. The result is the same.
This works with Claude Code, Codex or any other assistant that can run commands on your computer or, over SSH, on the server machine.
- Download
noodle-server-<version>.zipfrom the Download page and unzip it. You get anoodle-serverfolder. - Open a terminal in that folder and start the assistant (for example
claudeorcodex). - Paste the message below, after filling in the part in square brackets.
I want to install the Noodle server. This folder (noodle-server) is the package downloaded from the website. The server should run on: [for example "this computer", "the mini PC at home, reachable with ssh user@192.168.1.10", "my NAS"]. Read ISTRUZIONI.md in full (it is in Italian; talk to me in English) and follow it phase by phase, from start to finish: - first ask me the opening questions, one at a time, and explain in plain words what you are about to do; - if the ready-made image can't be pulled, go straight to path B (from source: docker compose up -d --build in the deploy folder); - before changing anything that already exists on the machine (firewall, proxy, other containers), show me what you will do and wait for my go-ahead; - after each phase run the check it describes and only move on if it passes; if it fails, look for the cause in the logs before trying again; - generate the tokens directly inside the .env file, without showing them in the chat or copying them into other files; just tell me how to read them so I can save them in my password manager. For Docker, Dockge and Nginx Proxy Manager details use deploy/README.md; use deploy/MCP.md only if I decide to connect assistants. When you finish, give me a summary: the address to open Noodle, where the data and backups live, how to update, and the final security checks, verified one by one.
The assistant runs the commands, but some things only you can do: choose or buy a domain, click through web dashboards (proxy, Tailscale), create the first account and turn on two-step verification, and save the tokens and recovery codes in your password manager. Keep this page open: the sections below explain every step, and the "Check" boxes help you not to skip any.
Work through sections 3 to 10 in order. Each phase ends with a check: only move on once it passes. The commands are for Linux; on a NAS the same steps can often be done from its Docker dashboard, but a terminal over SSH is still the simplest route.
If your user isn't in the docker group, put sudo in front of the docker commands.
03Installation
For now the way in is the downloadable package, from which Docker builds the image on your machine. Further down is the ready-made image route, for when the image is published to a registry.
From the downloadable package
Copy the zip to the server machine. From your computer, for example:
$ scp ~/Downloads/noodle-server-*.zip user@192.168.1.10:~Then, on the server machine, unzip the package into /opt and prepare the configuration:
$ sudo unzip ~/noodle-server-*.zip -d /opt $ sudo chown -R "$USER": /opt/noodle-server $ cd /opt/noodle-server/deploy $ cp .env.example .env && chmod 600 .env $ openssl rand -base64 32 # paste the output into NOODLE_TOKEN, then fill in the rest (see the table below) $ nano .env
If unzip is missing, install it (sudo apt install unzip on Debian and Ubuntu).
The .env variables
.env.example describes all of them (the comments are in Italian). These are enough to get started:
| Variable | What to put |
|---|---|
NOODLE_TOKEN | The admin token you just generated. It's used to create the first account and to open the /admin/ panel. If you leave it empty, the server generates one, saves it in data/token and prints it once in the log. |
NOODLE_SRC | .. (two dots), meaning the noodle-server folder as seen from deploy. The sample value /opt/noodle-src must be changed. With Dockge, or with the stack in another folder, use the absolute path, for example /opt/noodle-server. |
PUBLIC_URL | The address you open Noodle with, no trailing slash: http://192.168.1.10:4455 if it stays at home, https://noodle.yourdomain.com with a domain. Among other things it's used for links to published pages. |
TZ | Your time zone, for example Europe/London. |
NOODLE_PORT | The port on the host, 4455 by default. Only change it if it's already taken. |
NOODLE_DATA | The data folder, ./data by default (that is, deploy/data). It must be writable by uid 1000. |
COMPOSE_PROFILES | Leave it commented out or empty for now. Setting it to mcp turns on the gateway for AI assistants (section 7). |
TRUSTED_PROXY_IPS | Empty, until you put an HTTPS proxy in front of the server (section 6). |
Everything else has sensible defaults: attachments up to 200 MB, a backup every 6 hours keeping the last 30, plus one a day for 30 days. The SMTP_* variables are only needed if you want the server to send email (invitations, forgotten passwords); without them, invitation links are copied by hand.
Starting it
$ mkdir -p data && sudo chown 1000:1000 data $ docker compose up -d --build # the first time it builds the image: a few minutes $ docker compose logs -f # Ctrl+C to leave the logs (the server keeps running)
The first time a new version starts, the server may take a few minutes to prepare the data. That's normal, and Docker only reports it as healthy afterwards.
$ curl http://127.0.0.1:4455/api/healthWith Dockge
If you manage containers with Dockge: create a stack called noodle, paste the contents of deploy/docker-compose.yml, and in the .env section paste .env.example, filled in as above. NOODLE_SRC must be the absolute path of the package folder (for example /opt/noodle-server), because the Dockge stack lives in a different folder. Prepare the stack's data folder, then press Deploy:
$ sudo mkdir -p /opt/stacks/noodle/data $ sudo chown 1000:1000 /opt/stacks/noodle/data
With the ready-made image
Once the image is published to a registry, or if someone has already given you the image, there's nothing to build: all you need is the package's deploy/immagine/ subfolder. Copy it to the machine, for example to /opt/noodle, and run the script:
$ cd /opt/noodle $ sh installa.sh
The script creates the .env with a random admin token and gateway token, prints the admin token once (save it in your password manager straight away), pulls the image, starts the server and waits for it to respond. Running it again is safe: it leaves an existing .env and your data alone. If the image can't be pulled but is already on the machine, it uses that; otherwise it stops and points you to the package route.
On this route the data lives in the noodle-data Docker volume. If you'd rather have an ordinary folder, which is easier to copy, set NOODLE_DATA in the .env before the first start (the folder must be writable by uid 1000). Then check PUBLIC_URL and TZ, and after any change to the .env run docker compose up -d.
04First account
- In a browser, open
http://192.168.1.10:4455/with your machine's address. You'll see the "Crea il tuo account" (Create your account) screen. - Enter the admin token (the one in
NOODLE_TOKEN), a username and a password of at least 10 characters; a long phrase works well. This first account is the administrator. - Straight away, go to Impostazioni > "Sicurezza" (Settings > Security) and turn on a second factor: Face ID or Touch ID in the apps, a passkey in the browser over HTTPS, or an authenticator app with 6-digit codes. Without a second factor you can't connect assistants or protect notes.
- Save the 10 recovery codes in your password manager. Each one works once and lets you in without your phone.
- You create the other accounts yourself in Impostazioni > "Amministrazione" (Administration), either with an invitation or with a temporary password that each person changes when they first sign in.
You don't need the admin token day to day: after the first account you sign in with a username and password. Keep it anyway, because the /admin/ panel asks for it.
05Connecting the apps
Browser
Open the server's address and sign in. Noodle in the browser is served by the server itself, so it's already set up. Your pages are also kept in the browser and catch up when the connection comes back.
iPhone and Android
In Safari, open the server's address, then tap Share > Add to Home Screen: Noodle then opens like an app. On Android, Chrome only offers "Install app" when the address uses HTTPS. When the iPhone app becomes available, iOS will ask for permission to access the local network the first time: allow it, otherwise the app can't see a server at home.
Mac app
On first launch choose "Il mio server" (My server) and enter the address; if you're already using the app, go to Impostazioni > "Dati" > "Collega un server" (Settings > Data > Connect a server). The address takes the websocket prefix:
ws://192.168.1.10:4455on your local network, without HTTPS;wss://noodle.yourdomain.comwith HTTPS.
Then press "Accedi" (Sign in) and enter your username and password. Under Impostazioni > "Avanzate" (Advanced), "Verifica connessione" (Check connection) tells you whether the server is responding. Pages you created on the Mac before having a server are uploaded automatically the first time you sign in.
06Reaching it away from home
Pick based on the second question in section 1. If in doubt, Tailscale is the safest option for the least work.
Nothing else to do. Away from home Noodle keeps working on your devices from their local copies, and syncs as soon as you're back on your home network.
Just make sure your router doesn't forward port 4455 to the machine: the server must not be reachable from the internet without HTTPS.
With Tailscale the server stays invisible to the internet and only your own devices can reach it. Install Tailscale on the server machine and on your devices, signed in to the same account. From then on the server is reachable at its Tailscale address, for example ws://100.101.102.103:4455.
To get HTTPS as well, without opening any ports, turn on MagicDNS and HTTPS certificates in the Tailscale admin console, then on the server machine run:
$ sudo tailscale serve --bg 4455Noodle now answers at https://<machine>.<tailnet>.ts.net. Put that address in PUBLIC_URL, run docker compose up -d and use wss://<machine>.<tailnet>.ts.net in the apps. With HTTPS, passkeys and push notifications work in the browser too.
tailscale serve is a proxy as well: if every device shows up with the same IP under recent sign-ins in the /admin/ panel, set TRUSTED_PROXY_IPS as described in the "Domain with HTTPS" tab.
You need a proxy with an HTTPS certificate in front of port 4455, such as Nginx Proxy Manager or Caddy, and a domain name pointing at your public address. Sync runs over a websocket, which the proxy must let through.
Nginx Proxy Manager
Proxy Hosts > Add Proxy Host:
- Domain Names:
noodle.yourdomain.com - Scheme:
http; Forward Hostname / IP: the machine's IP (or the server container's name, if the proxy is on the same Docker network); Forward Port:4455 - Websockets Support: on. This is essential: without it the apps won't sync.
- Block Common Exploits: on
- SSL: a Let's Encrypt certificate, with Force SSL and HTTP/2 Support on
In the Advanced tab, paste this block, which allows attachments up to 200 MB and long-lived websocket connections:
client_max_body_size 210m; proxy_request_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s;
Caddy
Caddy gets the certificate on its own and passes the websocket through with no extra settings:
noodle.yourdomain.com {
reverse_proxy 127.0.0.1:4455
}The .env file
Set PUBLIC_URL=https://noodle.yourdomain.com, and set TRUSTED_PROXY_IPS to the proxy's IP as the container sees it. The server only reads the real client IP from connections that come from that address; it needs it for the logs and for throttling failed sign-in attempts. To find it, leave the variable empty, open Noodle through the proxy and look at the IP recorded under recent sign-ins in the /admin/ panel. It also accepts a subnet (CIDR) and several comma-separated values. Then run docker compose up -d.
NOODLE_PORT=127.0.0.1:4455: the port stays open to the host only, and the sole way in from outside is over HTTPS. A proxy running in a container (such as Nginx Proxy Manager) then reaches the server over the same Docker network, using the container's name (shown by docker compose ps).07Assistants over MCP
This part is optional. The MCP gateway is a separate container that brings your pages and your Brain into Claude.ai, Claude Code, Codex and any other assistant that speaks MCP. Each assistant only sees what you grant it, vault by vault, read-only or with write access.
How it's protected
- The gateway doesn't mount the server's data and never receives the admin token. It only talks to the server through an internal entry point, using a service token with limited permissions.
- It runs in a hardened container: read-only file system, no privileges, capped memory and processes, no access to the outside network.
- No password ever passes through the gateway: every connection is approved in the Noodle app, with two-step verification.
- An assistant can't change databases, can't delete anything permanently (it moves pages to the bin) and can't create more than 200 pages a day. Revoking access takes effect within half a minute.
Turning it on
In the .env, set:
COMPOSE_PROFILES=mcp MCP_SERVICE_TOKEN= MCP_PUBLIC_URL=https://mcp.yourdomain.com
MCP_SERVICE_TOKEN takes a random string, which you can get with openssl rand -hex 32 (on the ready-made image route the script has already generated it). MCP_PUBLIC_URL is the gateway's public HTTPS address, with no trailing slash and no /mcp; you'll choose it in the next step. Then restart the stack:
$ docker compose up -d --build # with the ready-made image, this is enough: docker compose up -d $ curl -s http://127.0.0.1:4456/.well-known/oauth-authorization-server
The last command should reply with JSON. The gateway only listens on 127.0.0.1:4456, so your local network can't see it.
Making it reachable
Cloud assistants have to reach the gateway from the internet over HTTPS on port 443 (other ports, such as 8443, may not work). You need something on the host that forwards to 127.0.0.1:4456:
- Tailscale Funnel:
sudo tailscale funnel --bg 4456publishes the gateway athttps://<machine>.<tailnet>.ts.net. After switching it on, the certificate can take a minute, so wait before testing. - Caddy on the host, with a dedicated subdomain:
mcp.yourdomain.com { reverse_proxy 127.0.0.1:4456 }
tailscale serve for the app on port 443 on the same machine, don't turn on Funnel on that port, or you'll make the app public too. Use Caddy with a subdomain instead, or a Tailscale node dedicated to the gateway, as described in deploy/MCP.md. The same file covers using a proxy that runs in a container.Put the resulting address in MCP_PUBLIC_URL and run docker compose up -d again. Then, from outside your home network, open the gateway's address followed by this path in a browser:
https://mcp.yourdomain.com/.well-known/oauth-authorization-server
Connecting assistants
- Claude.ai: Settings > Connectors > Add custom connector, with the address Noodle shows under Impostazioni > "Assistenti AI" (Settings > AI assistants; it's
MCP_PUBLIC_URLfollowed by/mcp). A page opens with a pairing code, for exampleK7QM-4F2T. In Noodle, under Impostazioni > "Assistenti AI", a request appears with the same code: choose what it may see and press "Approva" (Approve). Only approve if you've just started the connection yourself and the codes match. - Claude Code: Impostazioni > "Assistenti AI" > "Crea chiave" (Create key) gives you a key, shown only once, and the command to paste into a terminal:
Claude Code can also connect without a key, through the same code-based approval as Claude.ai.
$ claude mcp add --transport http noodle https://mcp.yourdomain.com/mcp --header "Authorization: Bearer <key>" - Codex and other MCP clients: same address and same key; the details are on the Integrations page.
Never paste the key into notes, chats or repositories: whoever holds it can read and change whatever it has been granted.
08Backup and restore
Automatic backups
The server takes a backup every 6 hours (BACKUP_INTERVAL_HOURS) and keeps the last 30, plus one a day for 30 days. Each .tar.gz archive holds the database, the attachments and a readable export of your pages. You'll find them:
- in
deploy/data/backups/on the package route; - in the
noodle-dataDocker volume, or in thebackups/folder underNOODLE_DATA, on the ready-made image route.
A copy off the machine
A backup on the same disk as the server won't save you from a disk failure. The backup folder is an ordinary path on the host, so copy it elsewhere with a scheduled job. For example, with crontab -e, every night at 3 a.m. to a mounted NAS:
0 3 * * * rsync -a /opt/noodle-server/deploy/data/backups/ /mnt/nas/noodle-backup/
rclone to a cloud service works just as well as rsync. Alternatively the server can run a command after every backup through BACKUP_HOOK; there are examples in .env.example.
The admin panel
At the server's /admin/ address (for example https://noodle.yourdomain.com/admin/) you'll find the server status, disk usage, recent sign-ins and the backups, with buttons to create one now, download them and restore them. It asks for the admin token and keeps it only in the open tab.
Restoring
In the panel, press "Ripristina" (Restore) next to the backup you want. Before restoring, the server automatically creates a pre-restore backup so you can go back. To restore an archive that isn't in the list, for instance on a new machine, copy it into the backup folder under its original name (noodle-YYYYMMDD-HHMMSS-reason.tar.gz) and reload the panel.
09Updating
Before updating it's worth pressing "Crea backup ora" in the /admin/ panel. Updating doesn't touch your data, and when the container stops the server saves every open document.
From the package
Download the new noodle-server-<version>.zip, copy it to the machine, unzip it into a temporary folder and copy the files over the old installation. The package contains neither .env nor data, so your configuration and data stay where they are.
$ rm -rf /tmp/noodle-new && unzip ~/noodle-server-*.zip -d /tmp/noodle-new $ cp -R /tmp/noodle-new/noodle-server/. /opt/noodle-server/ $ cd /opt/noodle-server/deploy $ docker compose up -d --build
With Dockge, once the files are copied, press Update on the stack to rebuild the image.
With the ready-made image
$ cd /opt/noodle $ docker compose pull && docker compose up -d
If you pinned a version with NOODLE_VERSION in the .env, change the number first.
The first time a new version starts, the server may take a few minutes to prepare the data. The check is the same as at install time: curl http://127.0.0.1:4455/api/health should show the new version.
10Final security checks
Tick each item once you've verified it. The boxes are remembered in this browser.
11Troubleshooting
The first place to look is always the log, from the compose folder:
$ docker compose ps $ docker compose logs --tail=100
| Symptom | Likely cause and fix |
|---|---|
The server doesn't answer on /api/health | The container is still starting: the first run of a new version can take a few minutes, and Docker waits up to 5 minutes before flagging a problem. If docker compose ps shows the container restarting over and over, the reason is in the log. |
The log shows EACCES or permission denied on /data | The container's user (uid 1000) can't write to the data folder: run sudo chown -R 1000:1000 data, then docker compose up -d. |
| The build stops straight away saying it can't find the context or the Dockerfile | NOODLE_SRC is wrong. From the deploy folder it should be ..; with Dockge, the absolute path of the package folder. |
| Port 4455 is already in use | Another program is using it: change NOODLE_PORT in the .env (for example to 4465) and use the new port in your addresses. |
| The browser opens Noodle but the status never reaches "Sincronizzato" (Synced) | The proxy isn't letting the websocket through. In Nginx Proxy Manager, turn on Websockets Support for the proxy host. |
| The connection drops after a minute or so | The proxy's timeouts are too short: add proxy_read_timeout 3600s; and proxy_send_timeout 3600s; in the Advanced tab. |
| Large attachments won't upload (error 413) | The proxy caps the request size: add client_max_body_size 210m; in the Advanced tab. |
| After a few wrong passwords nobody can sign in, and the panel shows the same IP for everyone | The server sees everyone with the proxy's IP, so the throttle blocks them all together. Set TRUSTED_PROXY_IPS as explained in section 6. |
| The browser doesn't offer passkey sign-in, or push notifications never arrive | Both need HTTPS. On your local network use an authenticator app as the second factor, or move to Tailscale or a domain with HTTPS. |
| A correct 6-digit code is rejected | Your phone's clock is off. Turn on automatic date and time. |
| You can't find the admin token | It's in the .env, under NOODLE_TOKEN. If you left that empty, the server saved it in the token file in the data folder. |
| The Mac app says the server can't be reached | Check the prefix: ws:// without HTTPS, wss:// with HTTPS. Away from home, without Tailscale or a domain, your home server isn't reachable. That's expected, and the app catches up when you're back. |
| Claude.ai can't add the connector | The gateway has to answer from the internet over HTTPS on port 443. Write MCP_PUBLIC_URL without a trailing slash and without /mcp. Right after turning on Funnel, wait a minute. The account must have two-step verification turned on. |
sh installa.sh says the image can't be pulled | The image hasn't been published to a registry yet, or NOODLE_REGISTRY and NOODLE_VERSION in the .env are wrong. Install from the package, as in section 3. |