Ollama
Videos
Overview: Ollama on Ubuntu 24.04 on Azure: overview (watch on YouTube)
Launch and log in: Ollama on Ubuntu 24.04 on Azure: launch and log in (watch on YouTube)
Deep Dive: Ollama on Ubuntu 24.04 on Azure: deep dive (watch on YouTube)
Overview
Ollama is a simple way to run open large language models on your own infrastructure. With a single command it downloads, quantizes and serves models such as Llama, Mistral, Gemma, Phi, Qwen and DeepSeek. It exposes a REST API that is also OpenAI chat-completions compatible (/v1/chat/completions).
This SecureAnyCloud image from the Azure Marketplace runs Ollama on Ubuntu 24.04 on Azure. It installs the official pinned Ollama release to /usr/local/bin/ollama and runs it under systemd. The Ollama server binds to loopback only (127.0.0.1:11434) and runs with no authentication of its own. nginx is the only network-facing surface: it terminates TLS on port 443 and enforces HTTP Basic authentication against a bcrypt htpasswd file.
No anonymous access. Ollama ships with no built-in authentication, so an open port is an open model API for anyone who can reach it. On the very first boot, this image generates a per-VM admin credential and writes it into the nginx htpasswd file. It also regenerates a fresh self-signed TLS certificate for this specific VM, with Subject Alternative Names that include the VM public IP and hostname.
Anonymous requests to the model API return HTTP 401 with a WWW-Authenticate: Basic challenge, and only the per-VM credential is accepted. The open GET /api/version health endpoint stays available for load balancer probes. The credential is written to /root/ollama-credentials.txt (mode 0600, root only).
CPU capable, GPU ready. The image runs models on CPU on any VM size. It ships Ollama's bundled CUDA runtime, so it auto-offloads inference to the GPU when launched on an NC or ND-series GPU VM with the NVIDIA driver present. A small starter model (llama3.2:1b) is pre-pulled so the API responds immediately.
What you get:
- Ollama installed to
/usr/local/bin/ollama, run under systemd as the unprivilegedollamasystem user, with the model store at/var/lib/ollama/models - The Ollama server bound to loopback only (
127.0.0.1:11434), never directly exposed to the network - nginx terminating TLS on :443 and enforcing htpasswd Basic auth on the model API, with
GET /api/versionand/healthzleft open for health probes - Port
:80returning a301redirect to HTTPS for every path except an unauthenticated/healthzendpoint (HTTP 200) - A per-VM admin credential generated on first boot and stored in
/root/ollama-credentials.txt(0600), with no anonymous access and no shared default password - A self-signed TLS certificate regenerated per VM on first boot (SAN includes the VM public IP and hostname). Replace it with your own CA-signed certificate for production
- A pre-pulled
llama3.2:1bstarter model so the API answers on launch, and one command to pull more models - A best-effort update to the latest Ollama release on first boot (offline-safe: the image ships a working pinned build regardless)
- An Ubuntu 24.04 LTS base with the latest security patches applied at build time
- The Azure Linux Agent for cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a guaranteed 24-hour response SLA
Prerequisites
You need an active Azure subscription, an SSH public key, and a VNet with a subnet in the target region. You also need a subscription to the Ollama listing on the Azure Marketplace.
Recommended virtual machine size: for real inference workloads, launch on a GPU VM. Standard_NC4as_T4_v3 (4 vCPU, 28 GB RAM, one NVIDIA T4) is a good entry point, and larger NC/ND-series VMs serve bigger models and more concurrency.
The image also runs on CPU-only sizes such as Standard_B4ms or Standard_D4s_v5, at lower throughput, for light use and testing. See Step 9 to enable GPU acceleration.
Deploy from the Azure Portal
Search the Azure Marketplace for Ollama, select the SecureAnyCloud publisher, and select Create.
Configure the network security group (NSG) with these rules, allowed from your client networks: TCP 22 (admin), TCP 443 (model API over HTTPS) and TCP 80 (HTTP-to-HTTPS redirect and health probe).
Deploy from the Azure CLI
RG="ollama-prod"; LOCATION="eastus"; VM_NAME="ollama-01" IMAGE_URN="sac1647283583153:ollama-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name ollama-vnet --address-prefix 10.100.0.0/16 --subnet-name ollama-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name ollama-nsg az network nsg rule create -g "$RG" --nsg-name ollama-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name ollama-nsg --name allow-https --priority 110 \
--destination-port-ranges 443 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name ollama-nsg --name allow-http --priority 120 \
--destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:ollama-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_NC4as_T4_v3 --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name ollama-vnet --subnet ollama-subnet --nsg ollama-nsg --public-ip-sku StandardFirst boot takes a few minutes. On its first boot, the VM finishes its per-VM setup in the background by generating its own credentials and starting the services. If you connect as soon as the VM shows as running, wait for that setup to complete before you continue:
bash sudo cloud-init status --wait
The command prints status: done when the VM is ready. The credentials and services described below exist only after setup completes.
Connect via SSH and retrieve your credential
The per-VM admin credential is generated on first boot and stored in a root-only file. Connect over SSH and read it:
ssh azureuser@<public-ip>
sudo cat /root/ollama-credentials.txtThe file contains the API URL, the username (admin), the per-VM password and quick-start commands. Store the password somewhere safe. It is unique to this VM.
Verify the Ollama and nginx services
Confirm that both services are active and that Ollama is bound to loopback only. In the listener output, 127.0.0.1:11434 (Ollama) is loopback only, while nginx listens publicly on :443 and :80:
systemctl status ollama nginx --no-pager ss -tln | grep -E ':11434|:443|:80 'Check the health endpoint
nginx serves GET /api/version without authentication, which suits load balancer and Azure health probes. The endpoint returns the Ollama version as JSON:
curl -s --cacert /etc/nginx/tls/ollama.crt https://127.0.0.1/api/version; echo
curl -k -o /dev/null -w 'healthz: HTTP %{http_code}\n' https://127.0.0.1/healthzList models and confirm anonymous access is rejected
Use the per-VM credential to list models through the API. Then confirm that an anonymous request, with no credential, is rejected with HTTP 401. The self-signed certificate lives at /etc/nginx/tls/ollama.crt, so pass it to curl with --cacert:
PASS=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
echo "# Authenticated model list:"
sudo curl -s -u "admin:$PASS" --cacert /etc/nginx/tls/ollama.crt https://127.0.0.1/api/tags | jq '.models[].name' echo "# Anonymous request (no credential) must be rejected:" curl -s -o /dev/null -w 'anonymous /api/tags -> HTTP %{http_code}\n' --cacert /etc/nginx/tls/ollama.crt https://127.0.0.1/api/tagsThe authenticated request returns the model list as JSON. The anonymous request returns HTTP 401, because this API allows no anonymous access.
Run a real completion (generate and chat)
Send a prompt to the pre-pulled model over the authenticated API. The /api/generate endpoint returns a completion. The OpenAI-compatible /v1/chat/completions endpoint accepts the same requests that any OpenAI SDK sends.
PASS=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-) sudo curl -s -u "admin:$PASS" --cacert /etc/nginx/tls/ollama.crt https://127.0.0.1/api/generate \
-d '{"model":"llama3.2:1b","prompt":"In one sentence, what is Ollama?","stream":false}' | jq -r '.response'The model returns generated text. On a CPU-only VM this takes a few seconds. On a GPU VM the response is near instant.
Issue the certificate for your public address
On first boot, the image writes the address that Azure reports into the TLS certificate, the credentials file and the login banner. With a Standard SKU public IP (the Azure default, and the --public-ip-sku Standard in Step 2), the current image records the VM's private address (for example 10.100.1.4). Clients that trust ollama.crt then refuse the certificate at the public IP (curl: (60) SSL: no alternative certificate subject name matches).
The current image also passes the address you call on to Ollama. Ollama answers HTTP 403 to any request not addressed to the VM itself, so every call to https://<public-ip>/ or a DNS name is refused, even with the right password. Check the address:
sudo grep '^OLLAMA_URL=' /root/ollama-credentials.txtIf the check shows a private 10. address, issue a new certificate for the public IP on the VM, and stop nginx from passing the public address to Ollama. Run the sed on the sac-ollama site line whatever the check shows. Azure's instance metadata service reports the public IP to the VM itself, and nothing leaves the VM:
PUBLIC_IP=$(curl -s -H Metadata:true 'http://169.254.169.254/metadata/loadbalancer?api-version=2020-10-01' | grep -o '"frontendIpAddress":"[^"]*"' | head -1 | cut -d'"' -f4)
PRIV=$(hostname -I | awk '{print $1}'); echo "public $PUBLIC_IP, private $PRIV"
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/nginx/tls/ollama.key -out /etc/nginx/tls/ollama.crt \
-subj "/C=GB/O=sac/CN=$PUBLIC_IP" \
-addext "subjectAltName=IP:127.0.0.1,DNS:localhost,DNS:$(hostname -s),IP:$PRIV,IP:$PUBLIC_IP" sudo chmod 0600 /etc/nginx/tls/ollama.key
sudo sed -i "s/\b$PRIV\b/$PUBLIC_IP/g" /root/ollama-credentials.txt /etc/update-motd.d/90-ollama
sudo sed -i -E 's/(proxy_set_header +Host +)\$http_host;/\1localhost;/' /etc/nginx/sites-available/sac-ollama
sudo systemctl restart nginxFor a DNS name, set PUBLIC_IP to the name and write DNS:$PUBLIC_IP in place of IP:$PUBLIC_IP. Your password does not change. Copy the new /etc/nginx/tls/ollama.crt to your clients as ollama.crt.
Pull more models and inspect them
Ollama can serve any model from its library. From a client, pull a larger model into your VM over the authenticated API. Replace <public-ip> and <password> with the values from /root/ollama-credentials.txt:
curl -u admin:<password> --cacert ollama.crt https://<public-ip>/api/pull -d '{"name":"llama3.2:3b"}'Inspect the models that are already in the local store on the VM:
sudo -u ollama OLLAMA_HOST=127.0.0.1:11434 ollama show llama3.2:1b
sudo -u ollama OLLAMA_HOST=127.0.0.1:11434 ollama listEnable GPU acceleration (NC/ND-series VMs)
The image runs on CPU by default and auto-offloads to the GPU once the NVIDIA driver is present. On a GPU VM, follow these steps.
Add the NVIDIA GPU Driver Extension to the VM. In the Portal, open the VM, go to Extensions and add NVIDIA GPU Driver Extension. Alternatively, run az vm extension set --name NvidiaGpuDriverLinux --publisher Microsoft.HpcCompute .... Reboot the VM when the extension completes.
Confirm the driver: nvidia-smi lists the GPU.
Restart Ollama with sudo systemctl restart ollama, then run a completion. Confirm the offload with sudo -u ollama OLLAMA_HOST=127.0.0.1:11434 ollama ps. PROCESSOR shows the GPU, and size_vram is greater than zero.
No image changes are needed. The same Ollama binary detects and uses the GPU.
Use the OpenAI-compatible endpoint from your apps
Point any OpenAI SDK at https://<public-ip>/v1 with basic auth. Example with curl:
PASS=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-) sudo curl -s -u "admin:$PASS" --cacert /etc/nginx/tls/ollama.crt https://127.0.0.1/v1/chat/completions \
-d '{"model":"llama3.2:1b","messages":[{"role":"user","content":"Say hello in French"}]}' | jq -r '.choices[0].message.content'From Python (LangChain, LlamaIndex or the OpenAI SDK), set base_url="https://<public-ip>/v1" and api_key="ollama". Also pass the admin and password Basic authentication pair, together with your CA-trusted or replaced certificate.
Use your own domain and a trusted certificate (production)
The self-signed certificate is a convenience for getting started. For production, front Ollama with your own DNS name and a CA-signed certificate.
Point a DNS record (for example ollama.example.com) at the VM public IP.
Replace /etc/nginx/tls/ollama.crt and /etc/nginx/tls/ollama.key with your CA-signed certificate and key, for example from Let's Encrypt or your internal CA. Then run sudo nginx -t && sudo systemctl reload nginx.
Clients can then call https://ollama.example.com/ with no certificate trust step.
Server components
- Runtime: Ollama (pinned at build, with a best-effort update to the latest release on first boot)
- Server binding: 127.0.0.1:11434 (loopback only, no authentication at this layer)
- Front proxy: nginx (TLS on :443, htpasswd Basic auth, :80 -> 301 https)
- Model store: /var/lib/ollama/models on the OS disk (llama3.2:1b pre-pulled)
- Credential: per-VM admin password, generated on first boot, in /root/ollama-credentials.txt (0600)
- TLS certificate: self-signed, regenerated per VM on first boot (SAN = public IP + hostname), in /etc/nginx/tls/
- API: REST (/api/*) and OpenAI-compatible (/v1/*)
- Operating system: Ubuntu 24.04 LTS (patched at build time)
- License: MIT (Ollama)
Managing the service
sudo systemctl status ollama --no-pager sudo journalctl -u ollama --no-pager -n 20
sudo systemctl reload nginxTo restart the runtime after a configuration change, run sudo systemctl restart ollama.
Model weights live under /var/lib/ollama/models and persist across service restarts and VM reboots. For large model libraries, attach an Azure data disk and mount it at /var/lib/ollama/models. To do so, stop ollama, move the data, update the mount and restart the service.
Security recommendations
Restrict the NSG. Allow TCP 443 (and 22 for admin) only from the client networks that need the API. Do not expose the model API to the public internet unless you intend to.
Rotate the credential. To change the admin password, run sudo htpasswd -B /etc/nginx/.ollama.htpasswd admin, then sudo systemctl reload nginx. Update /root/ollama-credentials.txt to match.
Add more users. Create additional accounts with sudo htpasswd -B /etc/nginx/.ollama.htpasswd <username>, then reload nginx.
Use a trusted certificate. For production, install one (Step 11) so that clients validate the endpoint without a manual trust step.
Keep the OS patched. Unattended security upgrades remain enabled on the running VM.
Support and Licensing
Ollama is distributed under the MIT License. This SecureAnyCloud image bundles the unmodified upstream Ollama release. SecureAnyCloud provides the packaging, hardening, per-VM credential and TLS automation, and 24/7 support with a guaranteed 24-hour response SLA.
Deploy on Azure
Find Ollama on Ubuntu 24.04 on the Azure Marketplace, published by SecureAnyCloud. Deploy it from the Portal or the Azure CLI as shown above.
Need Help?
Email sales@secureanycloud.com for deployment help, configuration questions or licensing enquiries.
Deploy on Azure
Launch Ollama on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
Ollama is a simple way to run open large language models locally. You pull and serve models such as Llama, Mistral, Gemma, Phi, Qwen and DeepSeek behind a REST API that is also OpenAI chat-completions compatible.
This SecureAnyCloud image runs Ollama 0.30 on Ubuntu 24.04 LTS with NVIDIA GPU acceleration. The NVIDIA datacenter driver is preinstalled and verified on real hardware during the build, and Ollama auto-detects the GPU to offload model inference.
The server listens on the loopback address 127.0.0.1:11434 under an unprivileged ollama system account. nginx fronts it on port 80 with HTTP Basic Authentication. The public /api/version endpoint stays open for health checks. Everything else, including model pull, generate, chat and the /v1/* OpenAI-compatible endpoints, requires the password. The default security group opens only port 22 (SSH) and port 80 (HTTP), so port 11434 is not reachable externally.
On the first boot of every deployed instance, a one-shot service generates a fresh password that is unique to that instance. It writes the password to /root/ollama-credentials.txt (mode 0600, root only).
Model weights live under /var/lib/ollama/models on a dedicated, independently resizable EBS data volume. A small starter model (llama3.2:1b) is pre-pulled so the API responds immediately. To add models, use ollama pull.
Prerequisites
- An AWS account subscribed to this product in AWS Marketplace.
- An EC2 key pair in your target region for SSH access.
- A security group that allows inbound TCP 22 (SSH) from your IP and TCP 80 (HTTP) from your users.
- An NVIDIA GPU instance type. Recommended:
g4dn.xlarge(NVIDIA T4, good value for 1B-8B models). For larger models or higher throughput, useg5/g6(A10G / L4) or multi-GPUg5.12xlarge.
Connecting to your instance
For the Ubuntu 24.04 variant, the login user is ubuntu. Example: ssh -i your-key.pem ubuntu@<instance-public-ip>
Step 1 - Launch from the AWS Marketplace console
- Open the product page in AWS Marketplace, choose Continue to Subscribe, then choose Continue to Configuration.
- Select the Ollama 0.30 on Ubuntu 24.04 delivery option and your region, then choose Continue to Launch.
- Choose a GPU instance type (
g4dn.xlargeor larger), your VPC and subnet, your key pair and the security group described above. Then launch the instance.
Step 2 - Launch from the AWS CLI
aws ec2 run-instances \
--image-id ami-xxxxxxxxxxxxxxxxx \
--instance-type g4dn.xlarge \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ollama}]'Step 3 - Connect to your instance
ssh -i your-key.pem ubuntu@<instance-public-ip>Step 4 - Confirm the GPU and services
nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader systemctl is-active ollama.service nginx.service curl -s http://127.0.0.1/api/versionExpected output:
Tesla T4, 610.43.02, 15360 MiB active active {"version":"0.30.7"}Step 5 - Retrieve your password
sudo cat /root/ollama-credentials.txt# Ollama - generated on first boot by ollama-firstboot.service OLLAMA_URL=http://<instance-public-ip>/ OLLAMA_USERNAME=admin OLLAMA_PASSWORD=<your-unique-password>Step 6 - List the pre-pulled model and pull more
The /api/version endpoint is open. Everything else requires HTTP Basic Authentication with the user admin and your password. List the starter model:
PASS=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s -u admin:$PASS http://127.0.0.1/api/tags | jq -c '.models[] | {name, size}'{"name":"llama3.2:1b","size":1321098329}Pull a larger model (for example Llama 3.2 3B):
curl -u admin:$PASS http://<instance-public-ip>/api/pull -d '{"name":"llama3.2:3b"}'Step 7 - Generate a completion (REST API)
PASS=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s -u admin:$PASS http://127.0.0.1/api/generate \
-d '{"model":"llama3.2:1b","prompt":"In one sentence, what is a large language model?","stream":false}' | jq -r .responseTo call the API from your own machine, target the instance's public IP instead of 127.0.0.1.
Step 8 - Use the OpenAI-compatible endpoint
Point any OpenAI SDK at http://<instance-public-ip>/v1 with HTTP Basic Auth:
from openai import OpenAI client = OpenAI( base_url="http://<instance-public-ip>/v1", api_key="ollama",
# ignored by Ollama default_headers={"Authorization": "Basic " + __import__("base64").b64encode(b"admin:<your-unique-password>").decode()}, ) resp = client.chat.completions.create( model="llama3.2:1b", messages=[{"role": "user", "content": "Hello!"}], ) print(resp.choices[0].message.content)The instance then works as a drop-in private LLM backend for LangChain, LlamaIndex or your own application.
Step 9 - Confirm GPU offload
PASS=$(sudo grep '^OLLAMA_PASSWORD=' /root/ollama-credentials.txt | cut -d= -f2-)
curl -s -u admin:$PASS http://127.0.0.1/api/ps | jq -c '.models[] | {name, size_vram}'A non-zero size_vram confirms that the model is running on the GPU rather than the CPU:
{"name":"llama3.2:1b","size_vram":1514584145}Enabling HTTPS
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comcertbot edits the nginx site at /etc/nginx/sites-available/sac-ollama to add the TLS listener, and it sets up automatic renewal.
Backup and maintenance
- Backups. Model weights live under
/var/lib/ollama/modelson a dedicated EBS volume. Snapshot that volume to back up pulled models, or re-pull them withollama pullon a new instance. - Password rotation. The password is in the nginx htpasswd file
/etc/nginx/.ollama.htpasswd. Rotate it withsudo htpasswd /etc/nginx/.ollama.htpasswd admin. - Model management. Pull models with the bundled CLI:
sudo -u ollama OLLAMA_HOST=127.0.0.1:11434 ollama pull mistral. List models withollama listand remove them withollama rm <model>. - Restarts and logs. Restart the service with
sudo systemctl restart ollama.service. View the logs withsudo journalctl -u ollama.service. - GPU sizing. A single model must fit in GPU memory (the T4 has 16 GB). For 70B-class models, use a larger or multi-GPU instance. Ollama falls back to CPU if a model does not fit, which is much slower.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and chat. Support covers Ollama deployment, model selection, GPU sizing, quantization, the OpenAI-compatible API, TLS termination and scaling. Contact details are on the AWS Marketplace listing.
Deploy on AWS
Launch Ollama on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com