New API
Overview
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). 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.
This guide shows you how to deploy and operate the SecureAnyCloud image of New API on Ubuntu 24.04 from the Azure Marketplace. The image ships the official upstream v1.0.0-rc.44 release binary, unmodified and pinned by checksum.
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.
Out of the box, the upstream release lets whoever reaches it first create the administrator through its setup wizard, and it 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 that the password works. It also proves that a wrong password is refused and that the wizard is closed, all before nginx is allowed to start.
New API is an independent open-source project by QuantumNous and its contributors. SecureAnyCloud produces this image. It is not affiliated with, endorsed by, or sponsored by the New API project or its contributors, and it ships the free, open-source, AGPL-3.0 licensed software unmodified.
What you get:
- New API v1.0.0-rc.44, the official upstream
linux/amd64release binary, pinned by sha256 and shipped unmodified, with itsLICENSE,NOTICEand 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
80only redirects to HTTPS - An nftables fence that keeps the New API port
3000reachable on loopback only, so nginx is the only way in - An embedded SQLite database owned by an unprivileged
new-apiservice 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
- 24/7 SecureAnyCloud support
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. This image therefore ships the current 1.0 candidate.
Prerequisites
Before you deploy this image, you need:
- 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_B2sVM or larger. The work New API does per request is authentication, routing and a streaming copy, so 2 vCPU and 4 GiB suit most teams - Inbound TCP
443(and optionally80, 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.
Deploy from the Azure Portal
- In the Azure Portal, search the Marketplace for New API on Ubuntu 24.04 LTS by SecureAnyCloud and select Create.
- Choose your subscription, resource group and region.
- Set the VM size to
Standard_B2sor larger. - Set the authentication type to SSH public key and supply your key.
- Under Inbound port rules, allow SSH (22) and HTTPS (443). Add HTTP (80) if you want plain
http://links to redirect. - On the Disks tab, choose
StandardSSD_LRSfor a good balance of cost and performance. - 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 it has proved that all of this worked.
Deploy from the Azure CLI
Accept the Marketplace terms, create the VM, and open port 443:
az vm image terms accept --urn sac1647283583153:new-api-ubuntu-24-04:default:latest
az vm create \
--resource-group my-resource-group \
--name new-api-gateway \
--image sac1647283583153: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 1001In production, restrict that rule to your own address ranges. A gateway holds your provider keys and spends on your provider accounts, so the fewer networks that can reach it, the better.
Connect to your VM
Connect over SSH:
ssh azureuser@<vm-ip>Confirm the services are running
Check that all four services are active:
sudo systemctl is-active new-api-loopback-fence.service new-api-firstboot.service new-api.service nginx.serviceExpected output:
active active active activenginx listens on ports 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. The appliance therefore adds a small nftables table that drops every packet to port 3000 unless it is addressed to loopback:
sudo nft list table inet sac_new_apiExpected output (the counters will differ):
table inet sac_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 this from the VM itself. Port 3000 answers on loopback but not 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 200HTTP 000 means the request was dropped. Nothing outside the VM can reach New API except through nginx.
Read your VM's credentials
Every credential on this appliance was generated for this VM on its first boot. Print the credentials file:
sudo cat /root/new-api-credentials.txtExpected 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 session secret and the encryption secret that New API uses for its own tokens are in /etc/new-api/new-api.env. Only root and the new-api service account can read it. You never need to type these secrets.
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 the certificate. Sign in as admin with NEWAPI_ADMIN_PASSWORD.
After you sign 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.
To set a password of your own, go to Security & Access > Change Password and record the new password somewhere safe. The credentials file keeps the original.
Understand the access model
The public status endpoint shows the state of the appliance:
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. First boot completed it 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 self-use mode off under System Settings > Operations and configure model prices.
- Every API call needs a key. The OpenAI-compatible endpoints under
/v1refuse requests without a valid key.
Each of these refuses anonymous callers:
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/modelsExpected output:
The system is already initialized New user registration has been disabled by administrator anonymous /v1/models -> HTTP 401Connect a model provider (a channel)
A channel is one upstream provider account. It has a type, a base URL if it is not the default, your API key for the provider, and the models it serves.
- In the console, open Channels and select Create Channel.
- Choose the provider. Built-in types include OpenAI, Anthropic, Gemini, Azure OpenAI, AWS Bedrock, Vertex AI and DeepSeek. For any OpenAI-compatible service, choose OpenAI and set its base URL.
- Paste your provider API key, select the models this channel should serve (or fetch them from the provider), and save.
- Use Test on the channel to send a real request through it.
Add several channels for the same model to spread load or fail over between accounts. Channel priority and weight control the routing. The appliance stores provider keys in its database on your VM's disk, where only the new-api service account can read them.
Create an API key for each application
Give every application its own key, so that you can see its usage and revoke it independently. In the console, open API Keys and select Create API Key. Give the key a name, and optionally a quota, an expiry date, an allowed model list or an IP allow list.
You can do the same from the command line on the VM. The following commands sign in with the administrator password from the credentials file, create a key named cli-app, print it, and use 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 .errorExpected 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. The router 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.
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 use 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 the client at that file (for the Python SDK, set SSL_CERT_FILE).
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 that 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.
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, use the webroot plugin of certbot (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 nginxFor the 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 can then use https://gateway.example.com/v1 with normal certificate verification.
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/SOURCEExpected 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 withsudo 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.serviceAzure Backup of the whole VM also captures the database. Keep /etc/new-api/new-api.env with any backup that you restore elsewhere, because New API needs the encryption secret in it to read what it 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 and read the release notes. Then replace
/opt/new-api/new-apiwith 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 that 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 its status:
sudo systemctl status new-api-firstboot.service --no-pager sudo journalctl -u new-api-firstboot.service --no-pager | tail -30The browser warns about the certificate. This is 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
SecureAnyCloud publishes and supports this image. For help with the image, deployment or configuration, contact SecureAnyCloud support 24/7 through the Azure Marketplace listing.
Deploy on Azure
Launch New API 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
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). New API exposes them to your applications through one OpenAI-compatible API, with an API key per application, quotas, model routing and a usage log of every call.
This guide shows you how to launch and use the SecureAnyCloud New API on Ubuntu 24.04 AMI on Amazon EC2. It is one version of the SecureAnyCloud MLOps and Speech AI listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.
New API hosts no model. It relays requests to the providers you connect, using your own provider accounts, so it needs no GPU. It is a single Go program with its web console built in and an embedded SQLite database, so the instance runs no database server and no container runtime.
Out of the box, the upstream release lets whoever reaches it first create the administrator through its setup wizard, and it allows open self-registration. On this image, both are closed before anything is reachable, and the gateway is fenced off from your network.
What you get:
- No first-caller takeover. First boot runs New API on loopback only, completes the setup wizard with an administrator password generated for your instance, turns self-registration off, and proves that the wizard and registration refuse anonymous callers. Only then is nginx allowed to start.
- HTTPS from the first boot. nginx serves the console and the API on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS. Requests for a host name that the instance does not serve are dropped.
- Secrets unique to your instance. First boot generates the administrator password, the session secret, the encryption secret that New API uses for stored provider keys, and the TLS key. None is baked into the image.
- Port 3000 is fenced. New API listens on port 3000 on every interface, because it has no loopback-only option. An nftables rule drops port 3000 on every interface except loopback, and on loopback only root and nginx may connect. nginx is the only way in.
- An egress fence against server-side request forgery. A channel's base URL is a URL that New API fetches, so the
new-apiservice account may reach public addresses only (your model providers). The instance metadata service, link-local addresses, loopback, private and reserved IPv4 ranges, your VPC's own address blocks and IPv6 are refused. You can allow a private model server (for example Ollama or vLLM in your VPC) in one file (Step 9). - Request hygiene at nginx. nginx passes the real client address and a fixed host name, and never a forwarding header that a client sends. It refuses any POST to the setup wizard, rejects encoded or doubled slashes and dot segments in the raw request line, refuses cross-site requests to the console, and rate-limits sign-in.
- Built from source and patched. SecureAnyCloud compiles New API v1.0.0-rc.48 from the unmodified upstream source with the current Go toolchain and six Go dependencies raised to clear Go security advisories. govulncheck finds no vulnerability that affects the binary. Ubuntu 24.04 LTS is fully patched at build time, and unattended security upgrades are on. No compiler or build tool is left on the image.
- On the image: New API v1.0.0-rc.48 (
/opt/new-api/new-api, with its web console built in), nginx, nftables, SQLite storage in/var/lib/new-api, the licence, notices and build provenance in/usr/share/doc/new-api, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent
New API is an independent open-source project by QuantumNous and its contributors. SecureAnyCloud produces this image. It is not affiliated with, endorsed by, or sponsored by the New API project, and it ships the free, open-source, AGPL-3.0 licensed software.
Default user: ubuntu
Architecture: x86_64
Recommended instance type: m5.large
Prerequisites
Before you deploy this image, you need:
- An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (New API must reach your model providers)
- A security group that allows TCP 22 (SSH), TCP 443 (the console and API) and TCP 80 (redirects to HTTPS) from your own networks only. A gateway holds your provider keys and spends on your provider accounts, so the fewer networks that can reach it, the better
- An account and API key for at least one model provider. You add these after launch
You are responsible for complying with the terms of every provider you connect, and with any legal or regulatory obligations that apply to the service you run with this gateway.
Launch the instance
In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick New API on Ubuntu 24.04 as the software version and select your Region, then choose Launch through EC2. Choose m5.large, your key pair and a security group as described above.
To launch with the AWS CLI instead, replace the values in angle brackets:
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=new-api}]'First boot takes well under a minute.
Connect to your instance
Version SSH user
New API on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>Check that the gateway answers
Check the gateway itself, not just the service state. The public status endpoint, reached through nginx on HTTPS, reports the version, that setup is complete, and that registration is off:
S=$(curl -sk https://127.0.0.1/api/status)
echo "$S" | jq '.data | {version, setup, register_enabled, self_use_mode_enabled}' [ "$(echo "$S" | jq -r '.data.setup')" = true ] && [ "$(echo "$S" | jq -r '.data.register_enabled')" = false ] && echo GATEWAY_OK{ "version": "v1.0.0-rc.48", "setup": true, "register_enabled": false, "self_use_mode_enabled": true } GATEWAY_OKPort 80 redirects to HTTPS. The New API port 3000 answers on loopback for root and nginx only. Any other user on the instance is refused, and from other machines port 3000 is dropped on every interface except loopback:
H=$(sudo sed -n 's#^NEWAPI_URL=https://\([^/]*\)/.*#\1#p' /root/new-api-credentials.txt)
curl -s -o /dev/null -w "http://$H/ -> HTTP %{http_code} %{redirect_url}\n" --resolve "$H:80:127.0.0.1" "http://$H/"
sudo curl -s -o /dev/null -w 'root to 127.0.0.1:3000 -> HTTP %{http_code}\n' http://127.0.0.1:3000/api/status
P=$(hostname -I | awk '{print $1}')
C=$(sudo -u nobody curl -s -o /dev/null -m 4 -w '%{http_code}' "http://$P:3000/api/status" || true)
echo "another user to the instance address :3000 -> HTTP $C" [ "$C" = 000 ] && echo FENCED_OKhttp://<your-instance-public-dns>/ -> HTTP 301 https://<your-instance-public-dns>/ root to 127.0.0.1:3000 -> HTTP 200 another user to the instance address :3000 -> HTTP 000 FENCED_OKHTTP 000 means the connection was refused. Nothing reaches New API except through nginx.
Get your URL and password
sudo cat /root/new-api-credentials.txtOnly root can read the file. Your values differ:
# New API v1.0.0-rc.48 - generated on first boot by new-api-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret. NEWAPI_URL=https://ec2-<public-ip-dashed>.compute-1.amazonaws.com/ NEWAPI_ADMIN_USERNAME=admin NEWAPI_ADMIN_PASSWORD=<24 characters generated on your instance> NEWAPI_TLS_SHA256_FINGERPRINT=<unique to your instance>
#
# Open NEWAPI_URL in a browser (self-signed certificate minted on this instance's first boot; compare its SHA-256 # fingerprint with the line above; port 80 redirects to https) and sign in as admin with the password above. # Self-registration is OFF. Add your upstream providers under Channels and create keys under API Keys; clients use # NEWAPI_URL + v1 (for example https://<host>/v1/chat/completions) with a token as the Bearer key. # Change the name it serves as:
sudo new-api-set-hostname <your-dns-name>
# Reach a model server inside your VPC: /etc/sac/new-api-egress-allow, then
sudo systemctl reload new-api-egress-guard
# User guide: https://secureanycloud.com/stacks/new-api/NEWAPI_URL uses the instance's public DNS name from EC2 instance metadata, and the certificate also names the public and private IP addresses.
The session secret and the encryption secret are in /etc/new-api/.env. Only root and the new-api service account can read it. You never need to type these secrets. To serve your own DNS name, see Step 11.
Sign in to the console
Open NEWAPI_URL in your browser. The certificate is self-signed for your instance, so the browser warns once. Compare the fingerprint it shows with NEWAPI_TLS_SHA256_FINGERPRINT before you accept the certificate. Sign in as admin with NEWAPI_ADMIN_PASSWORD. There is no sign-up link, because registration is off.
After you sign in, you land on the console. The admin sections (Channels, Models, Users and System Settings) are in the sidebar.
To set a password of your own, change it under Security & Access. The credentials file keeps the original.
The access model
The setup wizard, registration and the API all refuse anonymous callers, both at nginx and in New API itself. Test it:
B=https://127.0.0.1
H=$(sudo sed -n 's#^NEWAPI_URL=https://\([^/]*\)/.*#\1#p' /root/new-api-credentials.txt) R="--resolve $H:443:127.0.0.1"
SETUP=$(curl -sk $R -o /dev/null -w '%{http_code}' -H 'Content-Type: application/json' -d '{"username":"someone","password":"Some-password-1","confirmPassword":"Some-password-1"}' "https://$H/api/setup")
REG=$(curl -sk $R -H 'Content-Type: application/json' -d '{"username":"someone","password":"Some-password-1"}' "https://$H/api/user/register" | jq -r .message)
MODELS=$(curl -sk $R -o /dev/null -w '%{http_code}' "https://$H/v1/models")
echo "POST /api/setup -> HTTP $SETUP";
echo "POST /api/user/register -> $REG";
echo "anonymous /v1/models -> HTTP $MODELS" [ "$SETUP" = 403 ] && [ "$MODELS" = 401 ] && case "$REG" in *disabled*)
echo ACCESS_MODEL_OK ;; *) exit 1 ;; esacPOST /api/setup -> HTTP 403 POST /api/user/register -> New user registration has been disabled by administrator anonymous /v1/models -> HTTP 401 ACCESS_MODEL_OK- The setup wizard is closed. First boot completed it with your instance's administrator, so nobody else can claim it. nginx refuses any POST to it, and New API answers that it is already initialised.
- Self-registration is off. Only the administrator creates accounts (Users in the sidebar). Turn registration on under System Settings 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 self-use mode off and configure model prices.
- Every API call needs a key. The OpenAI-compatible endpoints under
/v1refuse requests without a valid key.
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.
- In the console, open Channels and add a channel.
- Choose the provider. Built-in types include OpenAI, Anthropic, Gemini, Azure OpenAI, AWS Bedrock, Vertex AI and DeepSeek. For any OpenAI-compatible service, choose OpenAI and set its base URL.
- Paste your provider API key, select the models this channel serves (or fetch them from the provider), and save.
- Use Test on the channel to send a real request through it.
Add several channels for the same model to spread load or fail over between accounts. Channel priority and weight control the routing. The database on your instance stores provider keys encrypted, and only the new-api service account can read it.
Create an API key for each application
Give every application its own key, so that you can see its usage and revoke it independently. In the console, open API Keys and create a key with a name, and optionally a quota, an expiry date, an allowed model list or an IP allow list.
You can do the same from the command line on the instance. The following commands sign in with the administrator password, create a key named cli-app, use it, and delete it again:
H=$(sudo sed -n 's#^NEWAPI_URL=https://\([^/]*\)/.*#\1#p' /root/new-api-credentials.txt) B="https://$H"; R="--resolve $H:443:127.0.0.1"
AT=$(sudo sed -n 's/^NEWAPI_ADMIN_PASSWORD=//p' /root/new-api-credentials.txt \
| jq -Rc '{username:"admin", password:.}' \
| curl -sk $R -H 'Content-Type: application/json' --data-binary @- "$B/api/user/login" | jq -r .data.access_token) A=(-H "Authorization: Bearer $AT" -H 'New-Api-User: 1') curl -sk $R "${A[@]}" -H 'Content-Type: application/json' \
-d '{"name":"cli-app","remain_quota":0,"expired_time":-1,"unlimited_quota":true}' "$B/api/token/" | jq -r '"created: \(.success)"'
ID=$(curl -sk $R "${A[@]}" "$B/api/token/?p=1&size=100" | jq -r '[(.data.items // .data)[] | select(.name=="cli-app")][-1].id')
KEY=$(curl -sk $R "${A[@]}" -X POST "$B/api/token/$ID/key" | jq -r .data.key)
echo "API key for cli-app: sk-${KEY:0:4}...(${#KEY} characters)"
M=$(curl -sk $R "$B/v1/models" -H "Authorization: Bearer sk-$KEY")
echo "$M"
E=$(curl -sk $R "$B/v1/chat/completions" -H "Authorization: Bearer sk-$KEY" -H 'Content-Type: application/json' \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello"}]}' | jq -r .error.code)
echo "chat completion before any channel: $E" curl -sk $R "${A[@]}" -X DELETE "$B/api/token/$ID" | jq -r '"deleted: \(.success)"' [ "$(echo "$M" | jq -r .success)" = true ] && [ "$E" = model_not_found ] && echo API_KEY_OKcreated: true API key for cli-app: sk-xxxx...(48 characters) {"data":[],"object":"list","success":true} chat completion before any channel: model_not_found deleted: true API_KEY_OKThe key is accepted and the request reaches the router. The router answers model_not_found because no channel serves that model yet. Once you add a channel (Step 7), /v1/models lists its models and the same request returns the provider's answer.
Point your applications at the gateway
Any OpenAI-compatible client works. Set the base URL to NEWAPI_URL followed by 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 use curl from your own machine, with the key in the NEWAPI_KEY environment variable:
curl https://<public-ip>/v1/chat/completions \
-H "Authorization: Bearer $NEWAPI_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. Until you install a certificate from a public authority (Step 11), clients must trust this instance's certificate. Copy /etc/nginx/ssl/new-api.crt to the client and point the client at that file (for the Python SDK, set SSL_CERT_FILE).
A model server inside your VPC. The egress fence lets New API reach public addresses only. To connect a private model server, for example an Ollama or vLLM host at 10.0.5.25, list its address in /etc/sac/new-api-egress-allow and reload the fence:
echo '10.0.5.25/32' | sudo tee -a /etc/sac/new-api-egress-allow
sudo systemctl reload new-api-egress-guardThe metadata service, link-local, loopback and multicast ranges are always refused and cannot be listed.
Security checks
List the egress fence in force for the new-api service account, with each rule's purpose:
sudo nft list table inet sac_new_api_egress | grep -oE 'comment "sac-[a-z0-9]+"' | sort -ucomment "sac-allow" comment "sac-allow6" comment "sac-fence3000" comment "sac-ipv6" comment "sac-local3000" comment "sac-loopback" comment "sac-metadata" comment "sac-metadata6" comment "sac-multicast" comment "sac-private" comment "sac-reply" comment "sac-vpc"The metadata service is refused to New API, while root on the same instance reaches it (the control). The same holds for the instance's own SSH port, which stands in for any service in your VPC:
I=$(sudo curl -s -m 4 -o /dev/null -w '%{http_code}' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token)
N=$(sudo setpriv --reuid=new-api --regid=new-api --clear-groups curl -s -m 4 -o /dev/null -w '%{http_code}' -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 60' http://169.254.169.254/latest/api/token || true)
P=$(hostname -I | awk '{print $1}')
VR=$(sudo timeout 4 bash -c "exec 3<>/dev/tcp/$P/22" 2>/dev/null && echo OPEN || echo REFUSED)
VN=$(sudo setpriv --reuid=new-api --regid=new-api --clear-groups timeout 4 bash -c "exec 3<>/dev/tcp/$P/22" 2>/dev/null && echo OPEN || echo REFUSED)
echo "metadata service: root HTTP $I, new-api HTTP $N"
echo "own address port 22: root $VR, new-api $VN" [ "$I" = 200 ] && [ "$N" = 000 ] && [ "$VR" = OPEN ] && [ "$VN" = REFUSED ] && echo EGRESS_FENCE_OKmetadata service: root HTTP 200, new-api HTTP 000 own address port 22: root OPEN, new-api REFUSED EGRESS_FENCE_OKnginx judges the raw request line before it decodes or normalizes the path, so encoded slashes, dot segments and doubled slashes are refused. The --path-as-is option sends the path exactly as written:
H=$(sudo sed -n 's#^NEWAPI_URL=https://\([^/]*\)/.*#\1#p' /root/new-api-credentials.txt) R="--resolve $H:443:127.0.0.1" OK=1 for p in '/api/%2e%2e/status' '/api//setup' '/api/../api/status' '/api/%2fsetup' '/API/SETUP'; do c=$(curl -sk $R --path-as-is -o /dev/null -w '%{http_code}' -X POST "https://$H$p")
echo "POST $p -> HTTP $c" case "$c" in 400|403|404) ;; *) OK=0 ;; esac done c=$(curl -sk $R -o /dev/null -w '%{http_code}' "https://$H/api/status");
echo "GET /api/status -> HTTP $c" [ "$OK" = 1 ] && [ "$c" = 200 ] && echo RAW_LINE_OKPOST /api/%2e%2e/status -> HTTP 400 POST /api//setup -> HTTP 400 POST /api/../api/status -> HTTP 400 POST /api/%2fsetup -> HTTP 400 POST /API/SETUP -> HTTP 403 GET /api/status -> HTTP 200 RAW_LINE_OKNew API runs as an unprivileged service account in a systemd sandbox, blocked from the metadata service. Only that account can read its database:
systemctl show new-api -p User -p NoNewPrivileges -p ProtectSystem -p IPAddressDeny
sudo stat -c '%U:%G %a %n' /var/lib/new-api/new-api.db /etc/new-api/.env /root/new-api-credentials.txtIPAddressDeny=192.0.0.0/24 fd00:ec2::254/128 168.63.129.16/32 169.254.169.254/32 240.0.0.0/4 198.18.0.0/15 224.0.0.0/4 User=new-api ProtectSystem=strict NoNewPrivileges=yes new-api:new-api 640 /var/lib/new-api/new-api.db root:new-api 640 /etc/new-api/.env root:root 600 /root/new-api-credentials.txtThere is no database server, so there is no database port to protect.
Your own DNS name and certificate
Point a DNS record at the instance, then set the name that the gateway is served as. The command mints a new self-signed certificate for the name and updates nginx, the New API trusted origins, and the URL and fingerprint in /root/new-api-credentials.txt. The name persists across reboots:
sudo new-api-set-hostname <your-domain>To use a certificate from your own or a public certificate authority instead, install it over the generated pair and reload nginx. Running new-api-set-hostname again replaces it with a self-signed certificate:
sudo install -m 0644 <your-domain>.crt /etc/nginx/ssl/new-api.crt
sudo install -m 0600 <your-domain>.key /etc/nginx/ssl/new-api.key
sudo nginx -t && sudo systemctl reload nginxWhat is in the image, and under what licence
Check the version, the checksum, the shipped documents and the source offer:
/opt/new-api/new-api --version sha256sum /opt/new-api/new-api ls /usr/share/doc/new-api sed -n '/^SOURCE OFFER/,/^$/p' /usr/share/doc/new-api/BUILD-PROVENANCE.txtv1.0.0-rc.48 0161240846b4e125d34d82a7c47908ffbf30ea18412c8768872922a2b8578057 /opt/new-api/new-api BUILD-PROVENANCE.txt LICENSE NOTICE THIRD-PARTY-LICENSES.md go.mod go.sum SOURCE OFFER (AGPLv3 sections 6 and 13): the corresponding source is the upstream tag below (https://github.com/QuantumNous/new-api/tree/v1.0.0-rc.48, commit 706144769e5bed5a47b78721e3bb11da3cf4c0b9) with go.mod and go.sum replaced by the two files in this directory. sac also provides it on request: sales@secureanycloud.com.New API is licensed under the GNU Affero General Public License v3.0, with attribution terms under section 7 in its NOTICE file, which the console honours.
BUILD-PROVENANCE.txt records exactly how the binary was built: the source archive and checksum, the Go toolchain, the raised dependencies with the resulting go.mod and go.sum (which ship beside it), and the build flags. It is also the AGPL source offer.
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.
Day to day operation
systemctl status new-api --no-pager | head -3 sudo journalctl -u new-api -n 20 --no-pager | tail -3- Restart the gateway with
sudo systemctl restart new-api, and reload nginx withsudo systemctl reload nginx.
- Data lives in
/var/lib/new-api/new-api.db(SQLite). Application logs are in/var/log/new-api.
- Back up by stopping the service briefly and copying the database. Keep
/etc/new-api/.envwith any backup you restore elsewhere: the encryption secret in it is needed to read the provider keys New API stored.
sudo systemctl stop new-api
sudo tar czf /root/new-api-backup-$(date +%F).tgz -C /var/lib/new-api . -C /etc/new-api .env
sudo systemctl start new-api- Upgrades. Unattended upgrades keep Ubuntu and nginx patched. New API itself is a single binary; to move to a newer release, back up first, read the upstream release notes, replace
/opt/new-api/new-apiand restart the service. New API migrates its database on start.
Troubleshooting
- Nothing answers on port 443. nginx starts only after first boot has completed. Check it with
sudo systemctl status new-api-firstboot --no-pagerandsudo journalctl -u new-api-firstboot --no-pager | tail -30. - The browser warns about the certificate. This is expected until you install your own certificate. Compare the fingerprint with
NEWAPI_TLS_SHA256_FINGERPRINT. - A request returns
model_not_found. No enabled channel serves that model for the key's group. Add the model to a channel (Step 7), or check that the channel is enabled and its test passes. - A channel test to a private address fails quickly. The egress fence refuses private and VPC addresses by default. Allow the server in
/etc/sac/new-api-egress-allow(Step 9). - A channel test to a public provider fails. The error comes from the provider. Check the key, the base URL, and that your account can use the model.
- HTTP 429 on sign-in. nginx allows 20 sign-in attempts a minute per address. Wait a minute and try again.
Security recommendations
- Allow ports 22, 80 and 443 in the security group from your own networks only.
- 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.
- Use your own DNS name and a certificate that your clients trust (Step 11).
- List only the private model servers you need in the egress allow-list.
- Keep the operating system patched. Unattended security upgrades are on.
Support and licensing
New API is open source under the AGPL-3.0, and there is no per-user licence fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24-hour response SLA
Deploy on AWS
Launch New API on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com