You’ve built the app. It works on your machine. The demo went well. Now someone asks the inevitable question:
“When can we use it for real?”
This is where a lot of internal projects stall. Not because the app isn’t ready, but because the deployment feels overwhelming. Kubernetes clusters, container orchestration, multi-region architectures, infrastructure-as-code templates with hundreds of lines…
None of that is necessary for most internal tools.
Here’s how I take an internal app from localhost to production on Azure, without overengineering it, and without a DevOps team.
## Why Azure?
If your organization is already running Microsoft 365, you’re halfway there.
Azure gives you:
- Single sign-on through Entra ID (formerly Azure AD)—your users already have accounts
- Managed hosting with Azure Web Apps—no servers to patch
- Secrets management with Key Vault—no credentials in code
- Tight integration with GitHub Actions for automated deployments
You don’t need to be an Azure expert. You need about four services configured correctly.
## Step 1: Azure Web Apps—Just Ship It
Azure Web Apps is the fastest way to get a Node.js (or Next.js) application running in the cloud without managing infrastructure.
What you get out of the box:
- HTTPS with a managed certificate
- Automatic OS and runtime patching
- Built-in scaling if you ever need it
- Deployment slots for staging and production
For most internal apps, the B1 or B2 tier is more than enough. You’re not serving millions of requests. You’re serving your team.
Create the resource, point it at your stack (Node 20 LTS works well), and you have a URL. That’s it.
Skip the container registry. Skip the Dockerfile. Azure Web Apps can deploy directly from your code.
## Step 2: Entra ID for Authentication—Use What You Already Have
This is the single biggest advantage of deploying an internal app on Azure.
Your users already log into Outlook, Teams, and SharePoint every day with their corporate identity. Entra ID lets your app use that same identity.
What this means in practice:
- No separate user database to manage
- No password reset flows to build
- No provisioning or deprovisioning—when someone leaves the company, they lose access automatically
- MFA comes for free if your tenant requires it
If you’re using Auth.js (formerly NextAuth), the Azure AD / Entra provider takes about 15 minutes to configure. Register an app in the Azure portal, grab the client ID and tenant ID, set the redirect URI, and you’re done.
Your users click “Sign in,” see the familiar Microsoft login screen, and they’re in. No friction, no training.
## Step 3: Environment Variables and Key Vault—Keep Secrets Out of Code
Every app has secrets: database connection strings, API keys, OAuth credentials. Hardcoding them is a liability. Committing them to Git is worse.
Azure Key Vault solves this cleanly:
- Store secrets in a secure, centralized vault
- Grant your Web App a managed identity so it can read secrets without storing any credentials itself
- Reference secrets directly in your Web App’s application settings
The pattern is simple. In the Azure portal, your app settings can reference Key Vault values using this syntax:
@Microsoft.KeyVault(SecretUri=https://your-vault.vault.azure.net/secrets/SECRET-NAME)
Your application reads them as normal environment variables. It never knows the difference, and the secrets never touch your codebase.
For local development, use a .env file (excluded from Git). In production, everything comes from Key Vault. Clean separation.
## Step 4: GitHub Actions—Automate the Boring Part
Manual deployments are a risk. You forget a step, deploy the wrong branch, or break something because production config was different from what you tested.
GitHub Actions eliminates that.
A basic deployment workflow looks like this:
- Push to
main - GitHub Actions builds the app
- Deploys to Azure Web Apps using the publish profile or a service principal
For most internal apps, a workflow file under 30 lines handles everything: install dependencies, build, deploy. No Docker. No Terraform. No multi-stage pipelines.
If you want a safety net, deploy to a staging slot first, verify it works, then swap to production. Azure Web Apps supports this natively, and it takes about two extra lines in your workflow.
The goal is that pushing code to main means it’s live within minutes. Predictable, repeatable, boring in the best way.
## Step 5: Custom Domain—Make It Real
Your app works. It’s deployed. It’s secured with Entra. But the URL is your-app.azurewebsites.net, and nobody will take that seriously.
Adding a custom domain takes a few minutes:
- Add a CNAME record pointing your subdomain to Azure
- Verify the domain in the Azure portal
- Bind it to your Web App
- Enable the free managed TLS certificate
Now your team is using approvals.yourcompany.com instead of a random Azure URL. It feels like a real product, because it is one.
## What You Don’t Need
This is just as important as what you do need.
For a typical internal app serving tens to hundreds of users, you do not need:
- Kubernetes—designed for scaling dozens of microservices across clusters. You have one app.
- Docker in production—useful in some cases, but Azure Web Apps deploys code directly. Skip the complexity.
- Terraform or Bicep—great for managing large infrastructure estates. For one Web App, one Key Vault, and one Entra registration, the portal works fine.
- Microservices—your internal tool is a single application. Keep it that way.
- Multi-region failover—if your internal app is down for 10 minutes, nobody’s going to lose revenue. Keep it simple.
Every layer of complexity you add is a layer you have to maintain. For internal tools, simplicity is a feature.
## The Whole Picture
Here’s what a complete, production-ready internal app deployment looks like:
- Azure Web App running your Next.js application on a B1/B2 plan
- Entra ID handling authentication—your users sign in with their existing corporate credentials
- Key Vault holding your secrets, referenced by the Web App through managed identity
- GitHub Actions deploying on every push to
main - Custom domain with a free managed TLS certificate
Five pieces. All managed services. No servers to patch, no containers to orchestrate, no late-night SSH sessions.
Total monthly cost for a typical internal app? Somewhere between a decent lunch and a mediocre dinner. The B1 plan starts around $13/month.
## Final Thought
The hardest part of deploying an internal app isn’t the technology. It’s fighting the urge to overcomplicate it.
You don’t need the architecture that Netflix uses. You need your app running, secured, and accessible to your team.
Azure makes this straightforward if you resist the temptation to turn a simple deployment into a science project.
Ship it. Let your team use it. Improve it based on what actually matters.