grpcUI on Ubuntu 24.04 on Azure User Guide
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
grpcuisystemd service - A bundled demo gRPC server (grpcbin) with server reflection enabled, running as the
grpc-demo-serversystemd 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.1only - nothing is exposed to the network directly except nginx grpcui.service,grpc-demo-server.serviceandnginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint 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) '

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.

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.

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.

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).

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.

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.

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.

Maintenance
- Password: the console password is set on first boot and stored as a bcrypt entry in
/etc/nginx/.grpcui.htpasswd. To change it, runsudo htpasswd -B /etc/nginx/.grpcui.htpasswd adminand thensudo 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, runsudo 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 on127.0.0.1:9000(plaintext) and127.0.0.1:9001(TLS, loopback only). It is only a reference target; you cansudo systemctl disable --now grpc-demo-serveronce 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:8080and the demo server to127.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.