Developer Tools Azure

grpcUI on Ubuntu 24.04 on Azure User Guide

| Product: grpcUI on Ubuntu 24.04 LTS on Azure

Overview

grpcUI is an interactive web user interface for gRPC servers - the graphical counterpart to command line tools such as grpcurl. You point it at a running gRPC service and it discovers that service's methods, either automatically through gRPC server reflection or from .proto/protoset files you supply, then renders an HTML form for each method. You fill in the request in the browser, invoke the method, and grpcUI performs the real gRPC call server-side and shows you the response message, the status code and the response metadata. In effect it is a Postman for gRPC.

The cloudimg image installs the pinned grpcUI 1.5.2 single Go binary running under systemd, and ships a small demo gRPC server (grpcbin) with server reflection enabled so the console lists callable methods out of the box. It is then locked down for a marketplace appliance: grpcUI is bound to loopback 127.0.0.1:8080, the demo server to 127.0.0.1:9000, and an nginx reverse proxy on port 80 adds a per-VM HTTP Basic Auth gate in front of grpcUI. grpcUI has no authentication of its own, so the nginx Basic Auth credential (user admin, unique password generated on first boot) is the gate. Backed by 24/7 cloudimg support.

What is included:

  • grpcUI 1.5.2 installed as a single Go binary and running as the grpcui systemd service
  • A bundled demo gRPC server (grpcbin) with server reflection enabled, running as the grpc-demo-server systemd service, so grpcUI immediately lists and can call methods
  • The grpcUI web console on :80, fronted by nginx with grpcUI bound to loopback only
  • Per-VM HTTP Basic Auth (user admin) protecting the console, with a unique password generated on first boot
  • grpcUI and the demo server bound to 127.0.0.1 only - nothing is exposed to the network directly except nginx
  • grpcui.service, grpc-demo-server.service and nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; grpcUI is light on resources. NSG inbound: allow 22/tcp from your management network, 80/tcp for the console, and 443/tcp if you add TLS. grpcUI serves plain HTTP on port 80; for production use, terminate TLS in front of it with your own domain and restrict access to trusted IP ranges (see Maintenance).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for grpcUI by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name grpcui \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name grpcui --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

systemctl is-active grpc-demo-server.service grpcui.service nginx.service

All three report active. grpc-demo-server is the bundled demo gRPC server (grpcbin, reflection on), bound to 127.0.0.1:9000; grpcui is the web console, bound to 127.0.0.1:8080; and nginx fronts grpcUI on port 80 with the per-VM HTTP Basic Auth gate. Only nginx listens on a public interface.

sudo ss -tlnp | grep -E ':(9000|9001|8080|80) '

The grpc-demo-server, grpcui and nginx services active, with the demo server on loopback 127.0.0.1:9000, grpcUI on loopback 127.0.0.1:8080, and nginx listening on port 80

Step 5 - Review the binaries and how grpcUI is wired

The image ships two pinned static Go binaries. grpcUI's systemd unit connects it to the demo server over plaintext on the loopback address, and the demo server binds only to loopback:

grpcui -version
grep ExecStart /etc/systemd/system/grpcui.service
grep -E 'insecure-addr|metrics-addr' /etc/systemd/system/grpc-demo-server.service

grpcUI reports grpcui v1.5.2, its ExecStart shows -plaintext -bind 127.0.0.1 -port 8080 127.0.0.1:9000, and the demo server binds its plaintext listener to 127.0.0.1:9000. To point grpcUI at your own gRPC service instead of the demo server, see Step 10.

grpcUI reporting version 1.5.2, its systemd ExecStart connecting -plaintext to the loopback demo server on 127.0.0.1:9000, and the demo server bound to loopback

Step 6 - Retrieve your web console password

nginx protects the grpcUI console with HTTP Basic Auth. The username is admin and a unique password is generated on the first boot of your VM and written to a root-only file:

sudo cat /root/grpcui-credentials.txt

This file contains GRPCUI_USERNAME, GRPCUI_PASSWORD and the GRPCUI_URL to open in a browser. The password is stored on disk only as a bcrypt hash in /etc/nginx/.grpcui.htpasswd, so no plaintext password ships in the image. Store the password somewhere safe.

Step 7 - Confirm the health endpoint and the auth gate

nginx serves an unauthenticated health endpoint for load balancers and probes, and gates everything else behind the per-VM password. The following reads the password from the credentials file and proves the round-trip - the health endpoint is open, an unauthenticated request is rejected, a wrong password is rejected, and the correct password authenticates:

PW=$(sudo grep '^GRPCUI_PASSWORD=' /root/grpcui-credentials.txt | cut -d= -f2-)
echo "healthz : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/healthz)"
echo "unauth  : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/)"
echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw http://127.0.0.1/)"
echo "authed  : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/)"

It prints healthz : 200, unauth : 401, wrongpw : 401 and authed : 200. The /healthz endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe, while the grpcUI console is only reachable with the per-VM password.

The per-VM credentials file with the password redacted, the health endpoint returning 200, and the Basic Auth round-trip returning 401 unauthenticated, 401 for a wrong password, and 200 with the per-VM password

Step 8 - Invoke a gRPC method from the shell through grpcUI

grpcUI exposes an HTTP invoke endpoint that performs the real gRPC call server-side. It is CSRF-protected, so you first fetch the page to obtain the _grpcui_csrf_token cookie, then POST the request to /invoke/<fully.qualified.Method>. The following invokes hello.HelloService/SayHello with a greeting of cloudimg on the bundled demo server:

PW=$(sudo grep '^GRPCUI_PASSWORD=' /root/grpcui-credentials.txt | cut -d= -f2-)
CJ=$(mktemp)
curl -s -c "$CJ" -u admin:$PW http://127.0.0.1/ -o /dev/null
TK=$(awk '/_grpcui_csrf_token/{print $NF}' "$CJ")
curl -s -u admin:$PW -b "$CJ" \
  -H 'Content-Type: application/json' \
  -H "x-grpcui-csrf-token: $TK" \
  -d '{"data":[{"greeting":"cloudimg"}]}' \
  http://127.0.0.1/invoke/hello.HelloService.SayHello \
  | jq -c '{status:.headers[0].value, reply:.responses[0].message.reply}'

grpcUI performs the gRPC call to the demo server and returns the response, so reply is hello cloudimg. Because grpcUI only exposes methods it discovered from the target via server reflection, a successful invoke proves the whole path - the browser form does exactly the same thing.

Invoking hello.HelloService/SayHello through grpcUI from the shell, returning a gRPC content type and the reply hello cloudimg

Step 9 - Explore and call methods in the web console

Browse to http://<vm-public-ip>/. Your browser prompts for a username and password: enter admin and the password from Step 6. grpcUI opens with a Service name and Method name selector already populated from the demo server's reflection data - addsvc.Add, grpc.gateway.examples.examplepb.ABitOfEverythingService, grpcbin.GRPCBin and hello.HelloService. Pick hello.HelloService and the SayHello method, and grpcUI renders a Request Form with a field for each message field (here, an optional greeting string).

The grpcUI explorer showing the hello.HelloService SayHello method selected, with a request form generated for the greeting field of the hello.HelloRequest message

Tick the checkbox next to greeting to include the field, type cloudimg, and the request is ready to send. The Raw Request tab shows the exact JSON grpcUI will use, and the Request Metadata section lets you add gRPC metadata (headers) to the call.

The grpcUI SayHello request form with the optional greeting field enabled and filled with the value cloudimg, ready to invoke

Click Invoke. grpcUI performs the gRPC call and switches to the Response tab, showing the response headers, the response data ({ "reply": "hello cloudimg" }) and any trailers - the same result you saw from the shell in Step 8, produced by a real call to the demo server.

The grpcUI Response tab showing the response headers with a gRPC content type and the response data reply hello cloudimg returned by the SayHello call

Step 10 - Repoint grpcUI at your own gRPC service

The demo server exists so the console works the moment it boots. To explore your own gRPC service, override the grpcUI target with a systemd drop-in and restart. If your service supports server reflection, that is all you need:

sudo systemctl edit grpcui

In the editor, add an override that replaces the ExecStart with your service's host:port (drop -plaintext and use -cacert/-cert/-key if your service uses TLS):

[Service]
ExecStart=
ExecStart=/usr/local/bin/grpcui -plaintext -bind 127.0.0.1 -port 8080 your-service-host:50051

Then apply it:

sudo systemctl restart grpcui

If your service does not support reflection, supply its schema instead by adding -proto yourservice.proto (with -import-path) or -protoset yourservice.protoset to the ExecStart line. grpcUI's other reflected services - grpcbin.GRPCBin and the rest - remain available on the demo server for reference.

The grpcUI explorer with the grpcbin.GRPCBin service and its DummyUnary method selected, showing a request form generated for a different service discovered via reflection

Maintenance

  • Password: the console password is set on first boot and stored as a bcrypt entry in /etc/nginx/.grpcui.htpasswd. To change it, run sudo htpasswd -B /etc/nginx/.grpcui.htpasswd admin and then sudo systemctl reload nginx.
  • Target service: grpcUI's target is set in /etc/systemd/system/grpcui.service (or your drop-in from Step 10). After changing it, run sudo systemctl daemon-reload && sudo systemctl restart grpcui. grpcUI reconnects and re-reads the schema on restart.
  • Demo server: the bundled demo server is grpc-demo-server.service, listening on 127.0.0.1:9000 (plaintext) and 127.0.0.1:9001 (TLS, loopback only). It is only a reference target; you can sudo systemctl disable --now grpc-demo-server once grpcUI points at your own service, but leave grpcUI's target updated first so it still has something to connect to.
  • Restrict access: grpcUI serves plain HTTP on port 80. For production, restrict access to trusted IP ranges in your Network Security Group, and front it with TLS (for example certbot with your own domain) terminating on :443.
  • Loopback binding: grpcUI is bound to 127.0.0.1:8080 and the demo server to 127.0.0.1, so nginx is the only path in. Keep it that way - do not change the bind address to a public interface; put access control in nginx or your NSG instead.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.