For IT teamsSelf-hosting

Self-hosting

Run the open source web app in your own environment for full control over where it runs.

The web app is open source, so you can run it on your own infrastructure. Your Microsoft Graph responses are then processed only by your own deployment, users sign in through your own app registration, and telemetry is off by default.

This page is for the IT team that will run the container.

Is self-hosting right for you?

Hosted web appSelf-hosted web appDesktop app
PriceFreeFreePaid
Where Graph responses are processedOur server, in memory onlyYour serverThe admin's computer
App registrationOursYours (single-page application)Yours (mobile and desktop)
UpdatesAutomaticYou pull new imagesInstalled when you choose
You operateNothingA container, HTTPS and DNSInstalls on admin computers

Self-hosting is a good fit if you want the free web app but your policy does not allow a third-party Entra app or a third-party server. If you prefer not to run a server at all, look at the desktop app.

Intune Documentation is licensed under the Elastic License 2.0. You may use, modify and self-host it, but you may not offer it to third parties as a hosted or managed service.

What you need

  • A host that runs Docker with Docker Compose.
  • A URL your users will open, for example https://intune-docs.example.com. Microsoft Entra ID requires HTTPS redirect URIs for anything other than localhost, so publish the app over HTTPS, for example behind your existing reverse proxy.
  • Permission to create an app registration and grant admin consent in Microsoft Entra ID.

Step 1: Create the app registration

The web app signs users in through an app registration in your tenant. It is a browser-based public client, so it needs no client secret.

Start a new registration

In the Microsoft Entra admin center, open Identity > Applications > App registrations and select New registration.

Give it a name your users will recognize, for example Intune Documentation.

Choose who can sign in

Under Supported account types, choose:

  • Accounts in this organizational directory only if only your own tenant will use it.
  • Accounts in any organizational directory if users from several tenants will use the deployment.

Add the single-page application platform

Open Authentication, select Add a platform and choose Single-page application. Enter a redirect URI that exactly matches the address users browse to, with no path and no trailing slash. For example http://localhost:3000 for a test on your own machine, or https://intune-docs.example.com in production.

The app uses the address in the browser as its redirect URI, so add one entry for every address users will use.

Use Single-page application

Do not choose Web or Mobile and desktop applications here. Sign-in fails with error AADSTS50011 or a similar redirect error if the platform or address does not match. The desktop app is the opposite: it needs Mobile and desktop applications, see Create the app registration.

Also keep localhost addresses off the other platforms of this registration. Microsoft ignores the port when it matches localhost redirect URIs, so an entry such as http://localhost under Web also matches http://localhost:3000 and sign-in fails with error AADSTS9002326.

Add the read-only permissions

Open API permissions, select Add a permission > Microsoft Graph > Delegated permissions and add:

  • User.Read
  • DeviceManagementConfiguration.Read.All
  • DeviceManagementApps.Read.All
  • DeviceManagementManagedDevices.Read.All
  • DeviceManagementRBAC.Read.All
  • DeviceManagementServiceConfig.Read.All
  • DeviceManagementScripts.Read.All
  • Group.Read.All

Optional: add Policy.Read.All if users will include Conditional Access policies. Adding it now means one admin consent covers everything.

All of these are read-only. Do not add application permissions.

Select Grant admin consent for your tenant and confirm with Yes. The Status column now shows that consent is granted for every permission.

Copy the IDs

On the Overview page, copy the Application (client) ID. For a single-tenant deployment, also copy the Directory (tenant) ID.

Step 2: Start the container

The image is published as ghcr.io/ugurkocde/intune-documentation with latest, X.Y and X.Y.Z tags.

Create the compose file

In a new directory on your Docker host, create compose.yaml:

compose.yaml
services:
  intune-documentation:
    image: ghcr.io/ugurkocde/intune-documentation:latest
    ports:
      - "3000:3000"
    restart: unless-stopped
    environment:
      AZURE_AD_CLIENT_ID: "${AZURE_AD_CLIENT_ID:?Set AZURE_AD_CLIENT_ID to your Entra app registration client ID}"
      AZURE_AD_TENANT_ID: "${AZURE_AD_TENANT_ID:-common}"

This is the same file as compose.yaml in the repository.

If a reverse proxy on the same host serves the app over HTTPS, publish the port on the loopback address only, so the plain HTTP port is not reachable from the network: "127.0.0.1:3000:3000".

Add your IDs

Create a .env file next to it:

.env
AZURE_AD_CLIENT_ID="your-entra-application-client-id"
# Optional. Defaults to "common".
AZURE_AD_TENANT_ID="common"

Set AZURE_AD_TENANT_ID to your tenant ID for a single-tenant deployment. Leave it as common if users from several tenants will sign in.

Start it

docker compose up -d

Open http://localhost:3000 on the host, or your HTTPS address through your reverse proxy. Select Sign in with Microsoft and run a collection to confirm everything works.

Environment variables

These are the only variables a self-hosted deployment needs. They are read when the container starts, so you can change them without rebuilding the image.

VariableRequiredDescription
AZURE_AD_CLIENT_IDYesThe application (client) ID of your app registration.
AZURE_AD_TENANT_IDNoYour tenant ID for single-tenant sign-in. Defaults to common.

The repository's .env.example lists more variables, for example for the hosted support form, desktop licensing and usage counters. Those run the public intunedocumentation.com service. Leave them unset for a self-hosted deployment.

Privacy and telemetry

A self-hosted deployment sends nothing to us.

  • No analytics. Plausible Analytics only loads on a configured public website origin with the analytics flag turned on. A self-hosted deployment runs in app mode with no third-party scripts.
  • No usage counters. The monthly-active-user and export counters need a Supabase database, which is not configured.
  • No support chat. The Crisp chat needs a website ID and a separate public origin, neither of which is set.
  • Same data handling as the hosted app. Your server collects, normalizes and redacts Graph responses in memory during the request and stores neither configuration nor tokens. Documents are built in the user's browser. See Security and privacy.

Updating

Pull the newest image and recreate the container:

docker compose pull
docker compose up -d

To control upgrades, pin a version tag such as X.Y.Z instead of latest in compose.yaml.

Troubleshooting

docker compose says "Set AZURE_AD_CLIENT_ID to your Entra app registration client ID"

The .env file is missing, is not next to compose.yaml, or does not set AZURE_AD_CLIENT_ID. Fix the file and run docker compose up -d again.

Sign-in reports "Microsoft Entra configuration is missing"

The container started without a client ID. Check the environment of the running container and restart it with AZURE_AD_CLIENT_ID set.

Sign-in fails with AADSTS50011 or a redirect URI error

The address in the browser does not exactly match a redirect URI on the app registration, or the URI was added under the wrong platform. Add the exact origin, for example https://intune-docs.example.com, under Single-page application.

Sign-in fails with AADSTS9002326 "Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type"

The app registration has a localhost redirect URI under another platform as well, for example http://localhost under Web next to http://localhost:3000 under Single-page application. Microsoft ignores the port when it matches localhost addresses and can pick the other platform's entry. Entra then rejects the token request from the browser because that entry is not a single-page application. Open Authentication, delete the localhost entries under Web and Mobile and desktop applications, and keep the address under Single-page application. If you also need the desktop app, give it its own app registration.

Users see an approval request or "Limited permissions detected"

Admin consent has not been granted for all permissions. Open the app registration, go to API permissions and select Grant admin consent again.

For other problems, see Troubleshooting or open an issue on GitHub.

Next steps

On this page