Artificial Intelligence (AI) Azure

New API on Ubuntu 24.04 on Azure User Guide

| Product: New API on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and operation of New API on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. New API is an open source, AGPL-3.0 licensed AI gateway. You connect upstream model services as channels (OpenAI, Anthropic, Google Gemini, Azure OpenAI, AWS Bedrock, Vertex AI, DeepSeek or any OpenAI compatible endpoint) and New API exposes them to your applications through one consistent API, with per application API keys, quotas, model routing and a usage log of every call.

New API hosts no model. It relays requests to the providers you connect, using your provider accounts, so it needs no GPU and runs comfortably on a small CPU only VM. It is a single Go executable with its web console built in and an embedded SQLite database, so the appliance runs no database server, no cache server and no container runtime.

The cloudimg image ships the official upstream v1.0.0-rc.44 release binary, unmodified and pinned by checksum. Out of the box the upstream release lets whoever reaches it first create the administrator through its setup wizard, and leaves open self-registration switched on. On this image both are closed on the VM itself before anything is reachable: first boot creates the administrator with a password generated for your VM, turns registration off, and proves the password works, a wrong one is refused and the wizard is closed before nginx is allowed to start. Backed by 24/7 cloudimg support.

New API is an independent open source project by QuantumNous and its contributors. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by the New API project or its contributors. It ships the free and open source AGPL-3.0 licensed software, unmodified.

The fence, New API and nginx services active, nginx listening on ports 80 and 443, and New API's port 3000 answering on loopback while a request to the VM's own address on port 3000 is dropped by the fence

What is included:

  • New API v1.0.0-rc.44, the official upstream linux/amd64 release binary, pinned by sha256 and shipped unmodified, with its LICENSE, NOTICE and third party notices in /usr/share/doc/new-api
  • The New API web console, built into the same binary
  • nginx serving the console and the API over HTTPS on port 443 with a certificate generated for your VM; port 80 only redirects to HTTPS
  • An nftables fence that keeps New API's own port 3000 reachable on loopback only, so nginx is the only way in
  • An embedded SQLite database owned by an unprivileged new-api service account; no database server is installed, so there is no database port to protect
  • A unique administrator password, session secret, encryption secret and TLS certificate generated per VM on first boot, never baked into the image

Why this release. At the time of this build the upstream project publishes its 1.0 release candidates as full releases and describes them as the last ones before 1.0. The older stable named 0.13 line is affected by security advisories that were fixed only in the 1.0 candidates, including a critical one, so this image ships the current 1.0 candidate.

Prerequisites

  • An Azure subscription with permission to create VMs
  • An account and API key for at least one model provider you want to connect (OpenAI, Anthropic, Google, Azure OpenAI, AWS Bedrock, DeepSeek or any OpenAI compatible service). You add these yourself after launch
  • A Standard_B2s VM or larger. New API's work per request is authentication, routing and a streaming copy, so 2 vCPU and 4 GiB suits most teams
  • Inbound TCP 443 (and optionally 80, which only redirects) open to the networks your applications and administrators connect from
  • An SSH key pair for administrative access

You are responsible for complying with the terms of every upstream provider you connect, and with any legal or regulatory obligations that apply to the service you run with this gateway in your jurisdiction.

Step 1: Deploy from the Azure Portal

  1. In the Azure Portal, search the Marketplace for New API on Ubuntu 24.04 LTS by cloudimg and select Create.
  2. Choose your subscription, resource group and region.
  3. Set the VM size to Standard_B2s or larger.
  4. Set the authentication type to SSH public key and supply your key.
  5. Under Inbound port rules, allow SSH (22) and HTTPS (443). Add HTTP (80) if you want plain http:// links to redirect.
  6. On the Disks tab, StandardSSD_LRS is a good balance of cost and performance.
  7. Select Review + create, then Create.

First boot takes well under a minute. During it the appliance generates your VM's credentials and certificate and closes the setup wizard and registration; nginx does not start until that has been proved.

Step 2: Deploy from the Azure CLI

az vm image terms accept --urn cloudimg1647283583153:new-api-ubuntu-24-04:default:latest

az vm create \
  --resource-group my-resource-group \
  --name new-api-gateway \
  --image cloudimg1647283583153:new-api-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --storage-sku StandardSSD_LRS \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port \
  --resource-group my-resource-group \
  --name new-api-gateway \
  --port 443 --priority 1001

Restrict that rule to your own address ranges in production. A gateway holds your provider keys and spends on your provider accounts, so the fewer networks that can reach it, the better.

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

Step 4: Confirm the services are running

sudo systemctl is-active new-api-loopback-fence.service new-api-firstboot.service new-api.service nginx.service

Expected output:

active
active
active
active

nginx listens on 80 and 443. New API listens on port 3000:

sudo ss -lntp | grep -E ':(80|443|3000)\b'

Expected output (process ids differ):

LISTEN 0  511      0.0.0.0:80     0.0.0.0:*  users:(("nginx",pid=3219,fd=5),...)
LISTEN 0  511      0.0.0.0:443    0.0.0.0:*  users:(("nginx",pid=3219,fd=7),...)
LISTEN 0  511         [::]:80        [::]:*  users:(("nginx",pid=3219,fd=6),...)
LISTEN 0  511         [::]:443       [::]:*  users:(("nginx",pid=3219,fd=8),...)
LISTEN 0  4096           *:3000         *:*  users:(("new-api",pid=3880,fd=11))

New API has no option to listen on loopback only, so the appliance adds a small nftables table that drops every packet to port 3000 unless it is addressed to loopback:

sudo nft list table inet cloudimg_new_api

Expected output (the counters will differ):

table inet cloudimg_new_api {
    chain input {
        type filter hook input priority filter - 10; policy accept;
        tcp dport 3000 ip daddr != 127.0.0.0/8 counter packets 0 bytes 0 drop
        tcp dport 3000 ip6 daddr != ::1 counter packets 0 bytes 0 drop
    }
}

You can prove it from the VM itself. Port 3000 answers on loopback and does not answer on the VM's own address, while nginx serves HTTPS:

curl -s -o /dev/null -w 'loopback 127.0.0.1:3000 -> HTTP %{http_code}\n' http://127.0.0.1:3000/api/status
curl -s -o /dev/null -m 4 -w "own address $(hostname -I | awk '{print $1}'):3000 -> HTTP %{http_code}\n" "http://$(hostname -I | awk '{print $1}'):3000/api/status" || true
curl -s -o /dev/null -w 'http://127.0.0.1/  -> HTTP %{http_code} %{redirect_url}\n' http://127.0.0.1/
curl -sk -o /dev/null -w 'https://127.0.0.1/ -> HTTP %{http_code}\n' https://127.0.0.1/

Expected output (your private address differs):

loopback 127.0.0.1:3000 -> HTTP 200
own address 10.0.0.11:3000 -> HTTP 000
http://127.0.0.1/  -> HTTP 301 https://127.0.0.1/
https://127.0.0.1/ -> HTTP 200

HTTP 000 means the request was dropped: nothing outside the VM can reach New API except through nginx.

Step 5: Read your VM's credentials

Every credential on this appliance was generated for this VM on its first boot.

sudo cat /root/new-api-credentials.txt

Expected output (your values differ):

# New API - generated for this VM on first boot by new-api-firstboot.service.
# Nothing here is shared with any other VM. Store the password somewhere safe;
# changing it in the console does not update this file.

NEWAPI_URL=https://<your VM's public IP>/
NEWAPI_ADMIN_USERNAME=admin
NEWAPI_ADMIN_PASSWORD=<24 characters generated on your VM>
NEWAPI_TLS_SHA256_FINGERPRINT=<unique to your VM>

# Sign in to the console at NEWAPI_URL. The certificate is self-signed for this VM, so
# your browser warns once; compare the fingerprint above before accepting it.
# OpenAI compatible base URL for your applications: https://<your VM's public IP>/v1
# (create one API key per application under API Keys). Open self-registration is OFF.

The per VM credentials file, readable only by root, holding the console URL, the admin username, the generated admin password and the certificate fingerprint

The session secret and the encryption secret New API uses for its own tokens are in /etc/new-api/new-api.env, readable only by root and the new-api service account. You never need to type them.

Step 6: Sign in to the console

Open NEWAPI_URL from the credentials file in your browser. The certificate is generated for your VM and is self-signed, so the browser warns once; compare the fingerprint it shows with NEWAPI_TLS_SHA256_FINGERPRINT before you accept it. Sign in as admin with NEWAPI_ADMIN_PASSWORD.

The New API sign in page served over HTTPS by the appliance, with the admin username entered and the password masked; there is no sign up link because open registration is off

After signing in you land on the overview, which walks you through creating a key and sending a first request. The admin sections (Channels, Models, Users and System Settings) are in the sidebar.

The New API console overview after signing in as admin, with the getting started guide, recommended actions and the full navigation including Channels, Models and Users

Change the administrator password under Security & Access → Change Password if you want one of your own, and record it somewhere safe; the credentials file keeps the original.

Step 7: Understand the access model

The appliance's state is visible on the public status endpoint:

curl -sk https://127.0.0.1/api/status | jq '.data | {version, setup, register_enabled, self_use_mode_enabled}'

Expected output:

{
  "version": "v1.0.0-rc.44",
  "setup": true,
  "register_enabled": false,
  "self_use_mode_enabled": true
}
  • The setup wizard is closed. It was completed on first boot with your VM's administrator, so nobody else can claim the instance.
  • Open self-registration is off. Only the administrator can create accounts (Users in the sidebar). Turn registration on under System Settings → Authentication only if you intend to run a service that people sign up to.
  • Self-use mode is on. Models can be called without first entering a price for each one, which suits a private gateway for your own team. If you will bill users by usage, turn it off under System Settings → Operations and configure model prices.
  • Every API call needs a key. The OpenAI compatible endpoints under /v1 refuse requests without a valid key.

Anonymous callers are refused by every one of these:

curl -sk -H 'Content-Type: application/json' -d '{"username":"someone","password":"Some-password-1","confirmPassword":"Some-password-1"}' https://127.0.0.1/api/setup | jq -r .message
curl -sk -H 'Content-Type: application/json' -d '{"username":"someone","password":"Some-password-1"}' https://127.0.0.1/api/user/register | jq -r .message
curl -sk -o /dev/null -w 'anonymous /v1/models -> HTTP %{http_code}\n' https://127.0.0.1/v1/models

Expected output:

The system is already initialized
New user registration has been disabled by administrator
anonymous /v1/models -> HTTP 401

The status endpoint reporting setup complete and registration off, the setup wizard and registration refusing anonymous callers, and the user, channel, key and model endpoints all answering 401 without credentials

Step 8: Connect a model provider (a channel)

A channel is one upstream provider account: its type, its base URL if it is not the default, your API key for it, and the models it serves.

  1. In the console, open Channels and select Create Channel.
  2. Choose the provider. Built in types include OpenAI, Anthropic, Gemini, Azure OpenAI, AWS Bedrock, Vertex AI and DeepSeek; choose OpenAI for any OpenAI compatible service and set its base URL.
  3. Paste your provider API key, select the models this channel should serve (or fetch them from the provider), and save.
  4. Use Test on the channel to send a real request through it.

The Create Channel dialog, listing built in provider types including OpenAI, Anthropic, Gemini, AWS Bedrock, DeepSeek, Azure OpenAI and Vertex AI

Add several channels for the same model to spread load or fail over between accounts; channel priority and weight control the routing. Provider keys are stored in the appliance's database on your VM's disk, readable only by the new-api service account.

Step 9: Create an API key for each application

Give every application its own key, so you can see its usage and revoke it on its own. In the console open API Keys, select Create API Key, give it a name, and optionally a quota, an expiry date, an allowed model list or an IP allow list.

The API Keys page with one key for an application, enabled, with an unlimited quota and the key masked

You can do the same from the command line on the VM. This signs in with the administrator password from the credentials file, creates a key named cli-app, prints it, and uses it:

PW=$(sudo sed -n 's/^NEWAPI_ADMIN_PASSWORD=//p' /root/new-api-credentials.txt)
B=https://127.0.0.1
AT=$(printf '{"username":"admin","password":"%s"}' "$PW" \
  | curl -sk -H 'Content-Type: application/json' --data-binary @- "$B/api/user/login" | jq -r .data.access_token)
printf '{"name":"cli-app","remain_quota":0,"expired_time":-1,"unlimited_quota":true}' \
  | curl -sk -H "Authorization: Bearer $AT" -H 'New-Api-User: 1' -H 'Content-Type: application/json' \
      --data-binary @- "$B/api/token/" | jq -r '"created: \(.success)"'
ID=$(curl -sk -H "Authorization: Bearer $AT" -H 'New-Api-User: 1' "$B/api/token/?p=1&size=100" \
  | jq -r '[(.data.items // .data)[] | select(.name=="cli-app")][-1].id')
KEY="sk-$(curl -sk -X POST -H "Authorization: Bearer $AT" -H 'New-Api-User: 1' "$B/api/token/$ID/key" | jq -r .data.key)"
echo "API key for cli-app: $KEY"
curl -sk "$B/v1/models" -H "Authorization: Bearer $KEY"; echo
curl -sk "$B/v1/chat/completions" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello"}]}' | jq .error

Expected output before any channel is connected (your key differs):

created: true
API key for cli-app: sk-oXRYx0CMMPBYdCwbwFi141Ij6dRB1oSUWm8XdpeWWdWBbnvw
{"data":[],"object":"list","success":true}
{
  "code": "model_not_found",
  "message": "No available channel for model gpt-4o-mini under group default (distributor) (request id: ...)",
  "type": "new_api_error"
}

The key is accepted and the request reaches the router; it answers model_not_found because no channel serves that model yet. Once you have added a channel in Step 8, /v1/models lists its models and the same request returns the provider's answer.

An application key listing models and reaching the router over the OpenAI compatible API, followed by the version and checksum of the shipped binary and its licence files

Step 10: Point your applications at the gateway

Any OpenAI compatible client works. Set the base URL to https://<your VM's address>/v1 and the API key to the key you created. For example, with the OpenAI Python SDK:

from openai import OpenAI

client = OpenAI(
    base_url="https://gateway.example.com/v1",
    api_key="sk-your-new-api-key",
)
reply = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)

Or with curl from your own machine:

curl https://<vm-ip>/v1/chat/completions \
  -H "Authorization: Bearer sk-your-new-api-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello"}],"stream":true}'

Streaming responses and WebSocket upgrades pass straight through nginx. New API also exposes provider native endpoints, such as the Anthropic Messages API at /v1/messages, so clients written for those providers can use the gateway too.

Until you install a certificate from a public authority (Step 12), clients must either trust this VM's self-signed certificate or skip verification. Trusting the certificate is the safer choice: copy /etc/new-api/tls/new-api.crt to the client and point it at that file (for the Python SDK, set SSL_CERT_FILE).

Step 11: Usage, users and quotas

  • Usage Logs records every call with the key, model, channel, tokens, cost and latency. Filter by key or model to see what each application used.
  • Users creates accounts for people who should manage their own keys. Each user's keys and usage are separate.
  • Quotas can be set on a user, and on each key, so a single application cannot exhaust your provider accounts.
  • Models shows which models your channels serve, and holds prices if you turn self-use mode off.

Step 12: Use your own domain and certificate

Point a DNS name at the VM's public IP, then replace the self-signed certificate with one from a public authority. For example, with certbot's webroot plugin (install certbot first):

sudo certbot certonly --webroot -w /var/www/html -d gateway.example.com
sudo ln -sf /etc/letsencrypt/live/gateway.example.com/fullchain.pem /etc/new-api/tls/new-api.crt
sudo ln -sf /etc/letsencrypt/live/gateway.example.com/privkey.pem /etc/new-api/tls/new-api.key
sudo nginx -t && sudo systemctl reload nginx

For that challenge to pass, add a location /.well-known/acme-challenge/ { root /var/www/html; } block to the port 80 server in /etc/nginx/sites-available/new-api.conf and open port 80. Clients then use https://gateway.example.com/v1 with normal certificate verification.

Step 13: What is in the image, and under what licence

/opt/new-api/new-api --version
sha256sum /opt/new-api/new-api
cat /usr/share/doc/new-api/SOURCE

Expected output:

v1.0.0-rc.44
d16189ecc6f0597c05e33e5e7ab8ee02e8ad2cc5085a9475f83b4793966a2d96  /opt/new-api/new-api
New API v1.0.0-rc.44, shipped UNMODIFIED as the official upstream release binary.
Source: https://github.com/QuantumNous/new-api/tree/633b4c5b190dc1af99a2814391354d86948e7943
Release: https://github.com/QuantumNous/new-api/releases/tag/v1.0.0-rc.44
Binary sha256: d16189ecc6f0597c05e33e5e7ab8ee02e8ad2cc5085a9475f83b4793966a2d96
Licence: GNU Affero General Public License v3.0 (see LICENSE and NOTICE in this directory).

New API is licensed under the GNU Affero General Public License v3.0. Its NOTICE file adds attribution terms under section 7 of that licence, which the unmodified console already honours. If you modify New API and let others use it over a network, the AGPL requires you to offer them the source of your modified version; read /usr/share/doc/new-api/LICENSE and /usr/share/doc/new-api/NOTICE before you do. The upstream project's source is at the address in SOURCE above.

Day to day operation

sudo systemctl status new-api.service --no-pager
sudo journalctl -u new-api.service -n 50 --no-pager
sudo ls -l /var/lib/new-api/
  • Restart the gateway with sudo systemctl restart new-api.service, and nginx with sudo systemctl reload nginx.
  • Data lives in /var/lib/new-api/new-api.db (SQLite with a write ahead log). Application logs are in /var/log/new-api.
  • Back up by stopping the service briefly and copying the database files:
sudo systemctl stop new-api.service
sudo tar czf /root/new-api-backup-$(date +%F).tgz -C /var/lib/new-api .
sudo systemctl start new-api.service

Azure Backup of the whole VM also captures the database. Keep /etc/new-api/new-api.env with any backup you restore elsewhere: the encryption secret in it is needed to read what New API stored.

  • Upgrades. Unattended upgrades keep Ubuntu and nginx patched. New API itself is a single binary; to move to a newer upstream release, back up first, read its release notes, replace /opt/new-api/new-api with the new release binary after checking its published checksum, and restart the service. New API migrates its database on start.

Security hardening

  • Restrict ports 443 and 80 to your own networks in the network security group. The gateway holds your provider keys.
  • Give each application its own key, with a quota and an expiry where it makes sense, and revoke keys you no longer use.
  • Keep registration off unless you intend to run a public service. If you turn it on, require email verification and set a quota for new users.
  • Use a certificate from a public authority for a real hostname (Step 12) so clients can verify the gateway.
  • Keep unattended upgrades on. They are enabled in this image and patch nginx, the only internet facing component.

Troubleshooting

Nothing answers on port 443. nginx starts only after first boot has completed. Check it:

sudo systemctl status new-api-firstboot.service --no-pager
sudo journalctl -u new-api-firstboot.service --no-pager | tail -30

The browser warns about the certificate. Expected until you install your own certificate; compare the fingerprint with NEWAPI_TLS_SHA256_FINGERPRINT in the credentials file.

The credentials file shows a private address. Azure did not expose the public IP to the VM when it first started. Use the VM's public IP from the Azure Portal instead; everything else is unaffected.

A request returns model_not_found. No enabled channel serves that model for the key's group. Add the model to a channel (Step 8) or check the channel is enabled and its test passes.

A channel test fails. The error comes from the provider: check the key, the base URL and that your provider account can use the model.

Support

This image is published and supported by cloudimg. For assistance with the image, deployment or configuration, contact cloudimg support 24/7 through the Azure Marketplace listing.