Local Deep Research on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and operation of Local Deep Research on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Local Deep Research is an open source research assistant: you ask a question, it breaks the question into searches, reads what it finds, and writes a report with the sources it used. This image runs the whole stack on one VM:
- Local Deep Research is the web application: research runs with live progress, research history, reports with citations, a document library and per user settings. Every account's data is stored in its own database, encrypted with that account's password (SQLCipher)
- SearXNG is a private metasearch engine on the same VM. Local Deep Research sends its web searches to it, and it queries public search engines on your behalf
- Ollama serves an open weight model on the VM, so research works without any API key
| Component | Version | Licence |
|---|---|---|
| Local Deep Research | 1.10.7 | MIT |
| SearXNG | commit b00212a8 (2026-10-14) |
AGPL 3.0 |
| Ollama | 0.40.4 | MIT |
Model granite4:micro (IBM Granite 4.0 Micro, 3B) |
registry manifest 89962fcc7523 |
Apache 2.0 |
The baked model is released under the Apache 2.0 licence, which permits commercial use and redistribution. Models you pull later carry their own licences; check each one before you use it commercially.
Set your expectations for the bundled model. Deep research is several rounds of searching, reading and writing, and granite4:micro is a small model running on the CPU. It answers straightforward factual questions with sources, but a multi step investigation with it is slow (several minutes for a single short run) and noticeably weaker than with a large model. For serious research, point Local Deep Research at a hosted model with your own API key (Step 11) or run the VM on a GPU size with a larger model (Step 12). The bundled model is there so the appliance works the moment it boots, privately and with no key.
No password, key or certificate is baked into the image. Upstream Local Deep Research lets anyone who can reach it register an account. This image turns self registration off, and first boot creates a single account, admin, with a password generated uniquely for each VM, together with SearXNG's secret, the app's session key and a TLS certificate. The web app does not start until first boot has done this, and first boot proves that an anonymous visitor is sent to the sign in page, that registration is refused and that a wrong password is refused before it marks the VM ready. Backed by 24/7 cloudimg support.
Local Deep Research is developed by LearningCircuit and contributors and is licensed under the MIT licence. SearXNG is developed by the SearXNG project and licensed under the GNU AGPL 3.0. Ollama is developed by Ollama, Inc. under the MIT licence, and Granite is a model family from IBM. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by LearningCircuit, the SearXNG project, Ollama or IBM. The names are used here solely to identify the software and model this image contains.

What is included:
- Local Deep Research 1.10.7 in its own Python environment, running as a dedicated
ldrservice account on127.0.0.1:5000 - SearXNG at a pinned commit, running as a dedicated
searxngaccount on127.0.0.1:8080, used only by Local Deep Research - Ollama 0.40.4 running as a dedicated
ollamaaccount on127.0.0.1:11434, with thegranite4:micromodel already pulled - nginx terminating HTTPS on
443with a per-VM certificate, redirecting80to HTTPS - Two command line helpers:
ldr-create-userto add accounts andldr-researchto run research from the shell - Every component pinned at build time: Python packages installed with
pip --require-hashesfrom upstream's own lock file, source trees checked against exact commits, the Ollama archive and the model checked against their published digests - Unattended security upgrades left on for the operating system

Prerequisites
- An Azure subscription with permission to create VMs
- A
Standard_B4msVM (4 vCPU, 16 GiB) or larger is recommended for the bundled model. The model alone uses about 3 GiB of memory while it works, and the app grows during a run; on a 4 GiB VM the model is stopped by the out of memory killer part way through a research run, so sizes below 8 GiB are not supported. More vCPUs make research faster:Standard_B8ms(8 vCPU, 32 GiB) or a D series size suits regular use and leaves room for a larger model. If you use a hosted model (Step 11) instead, the VM only runs the app and SearXNG, andStandard_B2ms(2 vCPU, 8 GiB) is enough - Inbound TCP
443(and80for the redirect) open only to the networks you connect from, plus22for SSH administration - An SSH key pair for administrative access
Step 1: Deploy from the Azure Portal
- In the Azure Portal, search the Marketplace for Local Deep Research on Ubuntu 24.04 LTS and select Create.
- Choose your subscription, resource group and region.
- Set the VM size to Standard_B4ms or larger.
- Under Administrator account, select SSH public key and supply your own key.
- Under Inbound port rules, allow HTTPS (443), HTTP (80) and SSH (22), then restrict all three to your own address ranges on the networking blade.
- Review and create.
Step 2: Deploy from the Azure CLI
az vm image terms accept --urn cloudimg1647283583153:local-deep-research-ubuntu-24-04:default:latest
az vm create \
--resource-group my-resource-group \
--name my-research \
--image cloudimg1647283583153:local-deep-research-ubuntu-24-04:default:latest \
--size Standard_B4ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard
# Open HTTPS (and the HTTP redirect) to YOUR network only.
MY_CIDR="203.0.113.0/24" # replace with your own range
az network nsg rule create \
--resource-group my-resource-group \
--nsg-name my-researchNSG \
--name allow-https-from-office --priority 1010 \
--source-address-prefixes "${MY_CIDR}" \
--destination-port-ranges 80 443 --protocol Tcp --access Allow
Step 3: Connect to your VM
ssh azureuser@<vm-ip>
Step 4: Confirm the services are running
First boot takes one to two minutes. It generates this VM's password, secrets and certificate, creates the admin account, starts the services and proves the sign in gate before it finishes.
# Every unit reports "active". The firstboot unit is a oneshot that stays active (exited).
systemctl is-active local-deep-research-firstboot local-deep-research searxng ollama nginx
# nginx is the only public listener; the app, SearXNG and Ollama listen on loopback.
sudo ss -lnt | grep -E ':(80|443|5000|8080|11434)\s' | awk '{print $4}' | sort
Expected output:
active
active
active
active
active
0.0.0.0:443
0.0.0.0:80
127.0.0.1:11434
127.0.0.1:5000
127.0.0.1:8080
[::]:443
[::]:80
Step 5: Read this VM's credentials
Everything in this file was generated on this VM at first boot.
sudo cat /root/local-deep-research-credentials.txt
# Local Deep Research - per-VM credentials generated on first boot (...).
# Keep this file private. This password also ENCRYPTS the admin account's database: there is
# no recovery if it is lost. Change it at https://<vm-ip>/auth/change-password .
LDR_URL=https://<vm-ip>/
LDR_USERNAME=admin
LDR_PASSWORD=<LDR_PASSWORD>
Keep this password safe. Local Deep Research encrypts each account's database with that account's password, so there is no password reset: if the password is lost, that account's research history cannot be read. You can change the password while you know it (Step 13).
Step 6: Sign in
- Open
LDR_URLfrom the credentials file in your browser. The certificate is self-signed and generated for this VM, so your browser will warn you the first time. Accept it for this address, or follow Step 14 to use your own domain and a trusted certificate. - Sign in as
adminwithLDR_PASSWORD. There is no sign up link: accounts are created by an administrator on the VM (Step 10).

Step 7: Understand the access model
- Accounts. Every page and API route needs a signed in account, except the sign in page and a health check. Self registration is switched off with
LDR_APP_ALLOW_REGISTRATIONS=falsein/etc/local-deep-research/ldr.env, so/auth/registersends everyone back to the sign in page. First boot created the only account,admin. Its research, settings and history are stored in its own database, encrypted with its password; the plain text password exists only in the root only credentials file. - Network. nginx is the only public listener: it serves the app on port
443with a certificate generated for this VM, and port80only redirects to HTTPS. The app (5000), SearXNG (8080) and Ollama (11434) all bind to127.0.0.1, so nothing but the VM itself can reach them. - Brute force. The app allows five password attempts per 15 minutes from one address, and nginx also limits sign in requests to ten a minute per client.
- Where your data goes. Prompts and reports are processed by the model on this VM. The research itself is web research: SearXNG sends the search terms Local Deep Research generates to public search engines, and the app fetches the pages it cites. If a question must not leave the VM, set the Egress Scope on the research form to Private only and research your own document collections instead (Library in the sidebar).
You can check the gate from the VM itself:
st() { curl -sk -o /dev/null -w '%{http_code} -> %{redirect_url}\n' "$@"; }
echo "anonymous / : $(st https://127.0.0.1/)"
echo "anonymous /auth/register : $(st https://127.0.0.1/auth/register)"
grep -E '^LDR_(APP_ALLOW_REGISTRATIONS|WEB_HOST)=' /etc/local-deep-research/ldr.env
# Sign in with this VM's password (the form needs its CSRF token).
J=$(mktemp)
T=$(curl -sk -c "$J" -b "$J" https://127.0.0.1/auth/login | grep -o 'name="csrf_token"[^>]*value="[^"]*"' | sed 's/.*value="//;s/"$//')
printf '%s' '<LDR_PASSWORD>' | curl -sk -c "$J" -b "$J" -e https://127.0.0.1/auth/login -o /dev/null \
-w 'sign in as admin : %{http_code} -> %{redirect_url}\n' \
--data-urlencode "csrf_token=$T" --data-urlencode username=admin --data-urlencode password@- https://127.0.0.1/auth/login
echo "signed in / : $(st -b "$J" https://127.0.0.1/)"
rm -f "$J"
Expected output:
anonymous / : 302 -> https://127.0.0.1/auth/login
anonymous /auth/register : 302 -> https://127.0.0.1/auth/login
LDR_WEB_HOST=127.0.0.1
LDR_APP_ALLOW_REGISTRATIONS=false
sign in as admin : 302 -> https://127.0.0.1/
signed in / : 200 ->

Step 8: The model and the search backend
Each new account starts with Ollama on this VM as its language model and the local SearXNG as its search engine. You can see the model and query SearXNG from the shell:
ollama --version
ollama list
ollama show granite4:micro --license | head -3
curl -s 'http://127.0.0.1:8080/search?q=boiling+point+of+water&format=json' \
| python3 -c 'import sys,json;r=json.load(sys.stdin)["results"];print("SearXNG results:",len(r))'
ollama version is 0.40.4
NAME ID SIZE MODIFIED
granite4:micro 89962fcc7523 2.1 GB ...
Apache License
Version 2.0, January 2004
SearXNG results: 20
The number of results varies with the query and with which public engines answer at the time. Local Deep Research refuses to query private or loopback addresses unless the server operator approves them, so /etc/local-deep-research/ldr.env approves exactly the local SearXNG with LDR_SEARCH_PRIVATE_ENGINE_URL_ALLOWLIST=http://127.0.0.1:8080.

The same settings are in the web app under Configuration (Settings) > Language Models, where the provider is Ollama and the model is granite4:micro.

Step 9: Run your first research
- Select New Research in the sidebar.
- Type a question in Research Query. Specific questions work best with the bundled model.
- Leave Research Mode on Quick Summary, and press Enter.

The progress page shows each step as it happens: the sub questions the model generates, the searches sent to SearXNG, the sources it reads. When the run finishes you get a report with numbered citations and a list of the sources it used. Every run is saved under History, and the report page has Download PDF and Export buttons.

Measured on a 2 vCPU, 8 GiB VM launched from this image, the Quick Summary run above took about 23 minutes, and the VM used at most 4.1 GiB of memory (the model about 3.2 GiB of it). A Standard_B4ms has twice the vCPUs, so expect roughly half that; a short question with the bundled model still takes several minutes. The report above also shows the limits of a 3B model: it is right about 100 °C, but its altitude figures are wrong (water does not boil at 210 °C anywhere on Earth). Check what it writes against its sources, or use a stronger model. New accounts start with one research iteration and two sub questions per iteration (search.iterations and search.questions_per_iteration in Settings) to keep runs short on the CPU; raise them for more thorough research once you use a hosted model or a GPU. Detailed Report mode writes a longer, sectioned report and takes considerably longer.
The banners at the top of the research page describe the run's egress scope. With the default Adaptive scope and SearXNG as the primary engine, the run uses public web search. Choose Private only to keep a run on the VM (your own document collections only).
Step 10: Add accounts for your team
Self registration is off, so an administrator adds accounts on the VM with ldr-create-user. It uses Local Deep Research's own account code, creates the account's encrypted database and applies the same research defaults as admin. Run it without a pipe to be prompted for the password; this example generates one:
NEWPW="$(openssl rand -hex 12)"
printf '%s\n' "$NEWPW" | sudo ldr-create-user analyst
echo "Give the analyst account this password: $NEWPW"
CREATE_USER_OK
Give the analyst account this password: ...
Passwords need at least 8 characters, a lowercase letter and a digit. Each account sees only its own research. Local Deep Research has no command to delete an account; cloudimg support can help you remove one safely.
If you would rather let people sign themselves up, set LDR_APP_ALLOW_REGISTRATIONS=true in /etc/local-deep-research/ldr.env and run sudo systemctl restart local-deep-research. Only do that while the VM is reachable solely from networks you trust, because anyone who can open the page can then create an account.
Step 11: Use a hosted model with your API key
For better research quality and speed, use a large hosted model. In the web app, open Configuration > Language Models:
- Set LLM Provider to the service you use: OpenAI API, Anthropic API, Google Gemini API, OpenRouter API, or OpenAI-Compatible Endpoint for Azure OpenAI and other compatible services (enter the endpoint URL).
- Paste your API key into that provider's API key field.
- Choose the model in LLM Model. The setting saves as you change it.
Settings are per account, so each user chooses their own provider and key. With a hosted model your prompts, and the page text the app sends to the model, go to that provider, under that provider's terms; web searches still go through the local SearXNG. You can stop Ollama to free its memory if no account uses it: sudo systemctl disable --now ollama.
Step 12: Add a larger local model, or use a GPU
Ollama can run any model from its library that fits in the VM's memory. Pull a model, then select it under Configuration > Language Models > LLM Model (the list is read from Ollama):
ollama pull qwen3:8b # about 5 GB; needs a VM with 16 GiB or more
ollama list
- Check each model's licence before using it commercially:
ollama show <model> --license. - The default research strategy (langgraph-agent) calls tools, so prefer models that support tool calling.
- Models are stored under
/var/lib/ollama/models. The OS disk has room for several more models; resize it in Azure if you plan to keep many large ones. Remove a model withollama rm <model>. - On a GPU VM size (NC or ND series) with the NVIDIA driver installed, for example through the Azure NVIDIA GPU Driver extension, Ollama uses the GPU automatically; its CUDA runtime is already included. That makes research with a larger model practical.
Step 13: Research from the shell, and change your password
ldr-research runs one question through the same API the web app uses and prints the report, so you can script research or run it from cron. Run as root it signs in with the admin credentials file; for another account set LDR_USERNAME and pass the password on standard input.
ldr-research --help | head -12
usage: ldr-research [-h] [--iterations ITERATIONS] [--questions QUESTIONS]
[--timeout TIMEOUT] [--set KEY=VALUE] [--json]
question
Run a Local Deep Research question from the shell.
...
A full run takes several minutes with the bundled model:
$ sudo ldr-research "What is the boiling point of water at sea level, and why does it drop at altitude?"
ldr-research: research 1f0c... started
ldr-research: [5s] in_progress 5%
...
ldr-research: [...s] completed 100%
# Boiling point of water at sea level
...
To change your password, sign in and open https://<vm-ip>/auth/change-password. Local Deep Research re-encrypts your database with the new password, so you need the current one; there is no reset.
Step 14: Use your own domain and certificate
The self-signed certificate is fine for a trial. For regular use, point a DNS name at the VM's public IP and install a trusted certificate. nginx already serves the Let's Encrypt HTTP-01 challenge path on port 80.
sudo apt-get update && sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/html -d research.example.com
sudo sed -i \
-e 's#/etc/local-deep-research/tls/server.crt#/etc/letsencrypt/live/research.example.com/fullchain.pem#' \
-e 's#/etc/local-deep-research/tls/server.key#/etc/letsencrypt/live/research.example.com/privkey.pem#' \
/etc/nginx/sites-available/local-deep-research
sudo nginx -t && sudo systemctl reload nginx
Certbot renews the certificate automatically; add --deploy-hook "systemctl reload nginx" to the certonly command so nginx picks up each renewal.
Step 15: Day-2 operations
# Restart the app, SearXNG or Ollama.
sudo systemctl restart local-deep-research
sudo systemctl restart searxng ollama
# Recent logs (add -f to follow them live).
sudo journalctl -u local-deep-research -u searxng -u ollama -n 20 --no-pager
# What is installed.
cat /usr/share/local-deep-research/VERSIONS
- Data. Everything the app stores is in
/var/lib/local-deep-research/data: the account list (ldr_auth.db), one encrypted database per account (encrypted_databases/), research outputs, caches and the app's session key. The app also keeps a daily encrypted backup of each account's database on login. Back up the whole directory, together with/var/lib/ollama/modelsif you pulled extra models, or take Azure disk snapshots of the VM. - Configuration. Server settings are in
/etc/local-deep-research/ldr.env(restart the app after a change). SearXNG's settings are in/etc/local-deep-research/searxng-settings.yml. - Operating system updates. Unattended security upgrades are on. Reboot when
/var/run/reboot-requiredexists. - Upgrading. Local Deep Research, SearXNG and Ollama are installed from pinned upstream releases. Take a snapshot first, then follow the upstream release notes; cloudimg support can help plan an upgrade.
Troubleshooting
- The browser shows a certificate warning. Expected with the per-VM self-signed certificate; see Step 14.
- The page does not load at all. Check that the VM's network security group allows
443from your address, and thatsystemctl is-active local-deep-research nginxreportsactive. - "Too many requests" when signing in. Five password attempts per 15 minutes are allowed from one address. Wait, then sign in with the password from the credentials file.
- A report says it cannot connect to the LLM service. The model server stopped, usually because the VM ran out of memory. Check
sudo journalctl -k | grep -i 'out of memory', use a size with at least 8 GiB (16 GiB recommended), and runsudo systemctl restart ollama. - Research is slow or shallow. Expected with a small model on the CPU. Use a hosted model (Step 11) or a GPU size with a larger model (Step 12). B series VMs are burstable; for sustained use, a D series size keeps its full CPU speed.
- Few or no search results. Public search engines sometimes rate limit or block requests from cloud addresses. Check
curl -s 'http://127.0.0.1:8080/search?q=test&format=json', and enable more engines in/etc/local-deep-research/searxng-settings.yml(see the SearXNG documentation), thensudo systemctl restart searxng.
Support
cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk, covering deployment, VM sizing, model and provider choice, research settings, accounts, TLS and upgrades.