GPT Researcher on Ubuntu 24.04 on Azure User Guide
Overview
GPT Researcher is an open source autonomous research agent. You give it a topic or a question; it plans its own set of research questions, searches the web, reads and summarises what it finds across many sources, and writes a long form report with inline citations and a reference list. Reports can be exported as Markdown, PDF or Word, and you can keep asking follow up questions against a finished report.
The distinction from a search box matters: a single run aggregates and cross checks many sources rather than answering from one page, which is what makes the output a research report rather than a search result.
GPT Researcher calls an AI provider, it does not host a model. That is what makes it cheap to run: no GPU, one small VM, and you bring your own provider key so you keep full control of which model you use and what it costs.
This cloudimg image runs GPT Researcher 3.5.1 installed from upstream source pinned to an exact commit, served by uvicorn behind nginx on port 80, with a private SearXNG instance for web search bound to the VM's loopback interface. Because GPT Researcher upstream has no sign in of any kind, cloudimg locks the whole instance behind a credential that every VM generates for itself on first boot, so an instance is never left open to strangers. Backed by 24/7 cloudimg support.
What is included:
- GPT Researcher 3.5.1 (
assafelovic/gpt-researcher, Apache-2.0), installed from upstream source pinned to commit409b8b60b477d50f2f844bc9598d1bcb7b056eeeso the shipped version never drifts - A private SearXNG search engine on the VM, so web search works out of the box with no search API key at all
- nginx on
:80enforcing HTTP Basic authentication against a per-VM credential, with response buffering disabled so the agent's progress streams live - A fail closed authentication gate: nginx refuses to start if the credential file is ever missing
- An unauthenticated
/healthzendpoint for load balancer and uptime checks - No AI provider key of any kind baked into the image — you add exactly one after launch
docker.service,nginx.service,gpt-researcher-search.service,gpt-researcher.serviceandgpt-researcher-firstboot.serviceas systemd units, enabled and active- 24/7 cloudimg support
How this image is secured
GPT Researcher upstream is designed for local, single user use and ships no authentication whatsoever. Deployed as upstream ships it, a VM on a public IP with a working provider key would let anyone who finds it spend your AI credits — and because one report is many model calls plus many page fetches, that is an expensive door to leave open. This image closes it four ways:
- nginx requires a credential on every path except the static
/healthzprobe. The username is randomised per VM and the password is 24 random characters, both generated on this VM at first boot and written to a root only file. There is no default, shared or documented credential at any point. - Port 80 stays shut until the credential exists.
nginx.servicecarries a condition on a marker file that first boot creates only after it has minted the credential and confirmed that an unauthenticated request is refused. A customer VM cannot serve a single request before it is locked down. - The gate is fail closed, not fail open.
nginx.servicealso runs a pre start assertion that re-checks the authentication directives and the credential file. If either is ever missing, nginx will not start at all rather than serving openly. Step 7 below demonstrates this on your own VM. - The application does not start until it is configured.
gpt-researcher.servicecarries a condition on the per-VM environment file, which is absent from the image, so the enabled unit is a no-op until first boot writes it.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2ms (2 vCPU / 8 GiB RAM) is the recommended size. The idle stack is modest, but a running report fans out concurrent workers that fetch and parse HTML and PDF sources in parallel while the LangChain runtime holds its models and embeddings in memory — 8 GiB gives that real headroom without a swap file. Go to Standard_B4ms for a busy shared instance or for deep research runs.
Network security group inbound rules: allow 22/tcp from your management network and 80/tcp from wherever you will use GPT Researcher. The image serves plain HTTP on port 80 — for anything reachable from the internet, terminate TLS in front of it (an Azure Application Gateway or Front Door, or your own certificate on the VM's nginx) and use a real hostname.
Nothing else needs opening. The application itself (8000) and SearXNG (8080) are both bound to the loopback interface and are unreachable from outside the VM.
You will also need an API key from one AI provider — OpenAI by default, or Anthropic, Google Gemini, Azure OpenAI, Groq, Mistral, Cohere, Together, any LiteLLM target, or a local Ollama endpoint. You do not need a search provider key: this image runs its own search engine.
Step 1 - Deploy from the Azure Marketplace
In the Azure portal choose Create a resource, search for GPT Researcher on Ubuntu 24.04 LTS by cloudimg, and select Create. Pick your subscription, resource group and region, set the size to Standard_B2ms, choose SSH public key authentication with username azureuser, and allow inbound 22 and 80.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group my-gptr-rg \
--name my-gptr \
--image /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Compute/galleries/<gallery>/images/gpt-researcher-ubuntu-24-04/versions/latest \
--size Standard_B2ms \
--storage-sku StandardSSD_LRS \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
az vm open-port --resource-group my-gptr-rg --name my-gptr --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-ip>
First boot generates this VM's credentials and locks the instance down before port 80 opens. It normally completes within a minute of the VM becoming reachable.
Step 4 - Confirm first boot completed and the services are running
sudo systemctl is-active gpt-researcher-firstboot.service
active
systemctl is-active docker.service nginx.service gpt-researcher-search.service gpt-researcher.service
active
active
active
active
All four report active. If gpt-researcher.service is not yet active, first boot is still running; wait a few seconds and repeat.

You can watch what first boot did:
sudo journalctl -u gpt-researcher-firstboot.service --no-pager | tail -20
The log shows the order that matters: the per-VM search secret and web credential are written first, the application is started and confirmed healthy, and only then is the marker created that lets nginx open port 80.
gpt-researcher-firstboot: Writing the per-VM SearXNG secret
gpt-researcher-firstboot: Starting the private web search stack
gpt-researcher-firstboot: VM address resolved to 10.0.0.13
gpt-researcher-firstboot: Minting the per-VM web credential into /etc/nginx/gpt-researcher.htpasswd
gpt-researcher-firstboot: Writing /etc/gpt-researcher/gpt-researcher.env (NO provider API key - the customer supplies exactly one)
gpt-researcher-firstboot: Starting gpt-researcher.service with the per-VM environment
gpt-researcher-firstboot: GPT Researcher is serving on the host loopback
gpt-researcher-firstboot: Creating the bootstrap-ready marker /var/lib/cloudimg/gpt-researcher-bootstrap-ready (this is what un-gates nginx)
gpt-researcher-firstboot: Starting nginx (:80 opens only now, already authenticated)
gpt-researcher-firstboot: Proving this VM refuses an UNAUTHENTICATED request with exactly 401
gpt-researcher-firstboot: Proving a one character mutated password is refused with exactly 401
gpt-researcher-firstboot: Proving the per-VM credential reaches GPT Researcher with exactly 200
gpt-researcher-firstboot: First boot complete; GPT Researcher is serving behind an authenticated nginx with per-VM secrets.
Step 5 - Confirm only port 80 is exposed
The application and the search engine must be reachable only from the VM itself. nginx is the single external surface.
sudo ss -lntp | grep -E ':(80|8000|8080)\s'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=8169,fd=5),...)
LISTEN 0 2048 127.0.0.1:8000 0.0.0.0:* users:(("uvicorn",pid=7997,fd=7))
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* users:(("docker-proxy",pid=7053,fd=8))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=8169,fd=6),...)
8000 (GPT Researcher) and 8080 (SearXNG) are bound to 127.0.0.1 only. Port 80 is the only one bound to a routable address.
Step 6 - Retrieve your per-VM sign in credential
sudo cat /root/gpt-researcher-credentials.txt
The file is 0600 root:root and contains the instance URL, the randomised username and the 24 character password generated for this VM. Store them in your password manager.

Step 7 - Confirm the instance refuses anonymous access
This is the check worth doing on every deployment. An unauthenticated request must be refused:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/
401
The health probe stays open so load balancers work without a credential:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz
200
Your own credential is accepted:
curl -s -o /dev/null -w '%{http_code}\n' -u '<GPTR_WEB_USERNAME>:<GPTR_WEB_PASSWORD>' http://127.0.0.1/
200

Step 8 - Add your AI provider key
No key ships in the image. GPT Researcher cannot run any research until you add one.
Uncomment the OPENAI_API_KEY= line and paste your own key. The same key covers both the language model and the embeddings GPT Researcher uses to rank sources. Either edit the file with sudo nano /etc/gpt-researcher/gpt-researcher.env, or do it in one step:
sudo sed -i 's|^# OPENAI_API_KEY=.*|OPENAI_API_KEY=<your-token>|' /etc/gpt-researcher/gpt-researcher.env
sudo systemctl restart gpt-researcher
The file is 0600 root:root, so your key is readable only by root.
To use a provider other than OpenAI, also set the model selectors in the same file — the commented examples show the pattern for Anthropic. Every provider LangChain supports is available; the selector format is provider:model-name.
Step 9 - Web search, with no search API key
Upstream GPT Researcher defaults to the paid Tavily search API. This image instead runs a private SearXNG instance on the VM and pins RETRIEVER=searx, so web search works with no second key and no external search subscription. Confirm it returns live results:
curl -s 'http://127.0.0.1:8080/search?q=azure+virtual+machine&format=json' -o /tmp/searx.json
head -c 300 /tmp/searx.json; echo
{"query": "azure virtual machine", "results": [{"template": "default.html", "title":
"Azure Virtual Machines", "content": "Create Linux and Windows virtual machines (VMs)
in seconds and reduce costs. Build, deploy, and run your applications on resilient and
scalable infrastructure.", "img_src": "",

Your only running cost is your own AI provider usage.
Step 10 - Sign in to the web interface
Open http://<vm-public-ip>/ in a browser. The instance asks for the username and password from Step 6 — this prompt is the appliance refusing anonymous access, and it is what stops a stranger running reports on your provider account.

After signing in you get the GPT Researcher interface: a research box, a report type selector, a tone selector and the source and result count controls.

Step 11 - Run your first report
Enter a research question, choose a report type and select the research button. The agent streams its progress live — the questions it planned, the sources it is reading, and then the report as it is written. A standard research report typically takes two to five minutes depending on the model and the breadth of the topic.

The agent's progress panel streams its work in real time — the research context it assembled from the web, the running cost of the run, and the point at which it starts writing the report.

When the report finishes you can copy it, or download it as Markdown, PDF or Word.
Step 12 - Confirm the fail closed gate
The authentication gate is not merely configured, it is enforced at start up. If the credential file is missing, nginx refuses to start rather than serving an open instance:
This block hides the credential file, proves nginx will not start without it, and puts everything back:
sudo systemctl stop nginx
sudo mv /etc/nginx/gpt-researcher.htpasswd /tmp/htpasswd.check
sudo systemctl start nginx 2>/dev/null && echo "UNEXPECTED: nginx started ungated" || echo "nginx REFUSED to start without a credential file"
sudo mv /tmp/htpasswd.check /etc/nginx/gpt-researcher.htpasswd
sudo chown root:www-data /etc/nginx/gpt-researcher.htpasswd
sudo chmod 0640 /etc/nginx/gpt-researcher.htpasswd
sudo systemctl reset-failed nginx
sudo systemctl start nginx
sudo systemctl is-active nginx
nginx REFUSED to start without a credential file
active
Step 13 - Confirm the shipped versions are the pinned ones
sudo cat /opt/gpt-researcher/.cloudimg-provenance
product.name=GPT Researcher
product.version=3.5.1
product.upstream=https://github.com/assafelovic/gpt-researcher.git
product.tag=v3.5.1
product.commit=409b8b60b477d50f2f844bc9598d1bcb7b056eee
product.licence=Apache-2.0
product.licence.sha256=c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4
search.image=searxng/searxng@sha256:854f239d5c181db9c4282cd0ffc1737a61232dd6ad0ea964028b96df17d7758a
search.image.tag_at_pin=2026.7.22-ef8f6470e
search.licence=AGPL-3.0-or-later
search.upstream=https://github.com/searxng/searxng
This records the exact upstream commit the application was built from, the sha256 of its Apache-2.0 licence file, and the immutable digest of the SearXNG image. The bundled component licences and their source locations are recorded here:
cat /usr/share/doc/cloudimg/THIRD-PARTY-NOTICES
Changing the sign in credential
To change the password for the existing user:
sudo htpasswd -bB /etc/nginx/gpt-researcher.htpasswd '<GPTR_WEB_USERNAME>' '<new-password>'
sudo systemctl reload nginx
Omit the final argument to be prompted for the password instead of passing it on the command line.
To replace the user entirely, use htpasswd -cB with a new username. Keep exactly one entry in the file unless you intend to share access.
Sharing the instance
This is a single tenant appliance: everyone who signs in shares one report library. Add a second htpasswd entry only if that is what you want.
Serving GPT Researcher on your own domain
Point a DNS record at the VM's public IP, then either terminate TLS on an Azure Application Gateway or Front Door in front of the VM, or add your own certificate to nginx at /etc/nginx/sites-available/cloudimg-gpt-researcher and open port 443.
Maintenance
Where your data lives:
sudo ls -la /var/lib/gpt-researcher
Reports, uploaded documents and logs are all under that directory. Back it up if the report history matters to you.
Security updates are applied automatically by unattended-upgrades. To check the service after a reboot:
systemctl is-active gpt-researcher gpt-researcher-search nginx
To view application logs:
sudo journalctl -u gpt-researcher --no-pager | tail -20
Support
Every cloudimg image includes 24/7 support. Contact support@cloudimg.co.uk with your Azure subscription ID and the VM name.