What every host needs
A MauiWeb site is an ordinary ASP.NET Core app, so it runs anywhere ASP.NET Core runs. Four things matter:
- WebSockets allowed through every proxy, for
/_maui/ws. - Sticky sessions when you run more than one instance: each visitor's app lives on one instance, and their socket must reach it.
- Long-lived connections: raise proxy and platform timeouts. If a socket is cut, the page reconnects to the same session within 45 seconds.
- The real host name forwarded to your app, so the license check sees your domain.
Add your license key as the MAUIWEB_LICENSE_KEY environment variable, not in your repository. Health checks can use GET /_maui/health.
Build a container
Publish with NativeAOT so the server runs one native binary and no .NET assemblies. This Dockerfile sits at your solution root, next to the shared app and the web project:
FROM mcr.microsoft.com/dotnet/sdk:11.0-aot AS build WORKDIR /src COPY . . RUN dotnet publish MyApp.Web/MyApp.Web.csproj -c Release -r linux-x64 -p:PublishAot=true -o /app FROM mcr.microsoft.com/dotnet/runtime-deps:11.0 WORKDIR /app COPY --from=build /app . ENV ASPNETCORE_URLS=http://+:8080 EXPOSE 8080 ENTRYPOINT ["./MyApp.Web"]
Fonts, images and raw files copied into the web project's Resources folder at build time are published with it. See Share your app.
Google Cloud Run
Cloud Run builds the Dockerfile and runs the container. Turn on session affinity, raise the request timeout to the 60-minute maximum (it also limits how long a WebSocket stays open) and keep one instance warm so visitors' sessions are not lost when the service scales to zero.
gcloud run deploy myapp-web --source . --region us-central1 \ --allow-unauthenticated --port 8080 \ --session-affinity --timeout 3600 --min-instances 1 \ --set-secrets MAUIWEB_LICENSE_KEY=mauiweb-license:latest
Cloud Run's session affinity is best effort, so keep per-visitor data you cannot lose in a database, as you would for any web app. Map your domain under Cloud Run's custom domains, then make sure it is on your license.
Azure App Service
Push the image to Azure Container Registry and create a Linux web app from it in the portal or with az webapp create. Then turn on WebSockets, Always On and ARR affinity (sticky sessions), and set the port and key:
az acr build --registry myregistry --image myapp-web:1 . az webapp config set -g my-group -n myapp-web --web-sockets-enabled true --always-on true az webapp update -g my-group -n myapp-web --client-affinity-enabled true az webapp config appsettings set -g my-group -n myapp-web \ --settings WEBSITES_PORT=8080 MAUIWEB_LICENSE_KEY="MW1.…"
Always On needs a Basic plan or higher. Add your custom domain under Custom domains, then make sure it is on your license.
Your own Linux server
This is how the live gallery runs: the NativeAOT binary as a systemd service behind nginx. Publish on a Linux machine (dotnet publish -c Release -r linux-x64 -p:PublishAot=true -o out) and copy out to the server.
[Unit] Description=MyApp website (MauiWeb) After=network.target [Service] WorkingDirectory=/var/www/myapp-web ExecStart=/var/www/myapp-web/MyApp.Web --urls http://127.0.0.1:5380 Environment=ASPNETCORE_ENVIRONMENT=Production EnvironmentFile=/etc/myapp-web.env Restart=always User=www-data [Install] WantedBy=multi-user.target
map $http_upgrade $connection_upgrade { default upgrade; "" close; }
server {
listen 443 ssl;
server_name shop.example.com;
# ssl_certificate lines from certbot
location / {
proxy_pass http://127.0.0.1:5380;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 1h;
proxy_send_timeout 1h;
client_max_body_size 100m; # FilePicker and MediaPicker uploads
}
}
Put MAUIWEB_LICENSE_KEY=… in /etc/myapp-web.env, then run systemctl enable --now myapp-web and reload nginx. With more than one server, use a load balancer with sticky sessions in front of them. Behind Cloudflare, WebSockets work by default; uploads are limited to 100 MB per request on its Free and Pro plans.
Domains and your license
- Your key lists the domains it covers.
example.comandwww.example.comcount as one domain. - On a domain the key does not cover, the site keeps working and visitors see an "Unlicensed MauiWeb" notice.
- About once a day your server reports the key's id, the domain and the MauiWeb version to our license service. Allow outgoing HTTPS from your server.
- Professional and Enterprise keys can include staging domains. See pricing and license key.