Skip to content

Getting Started with Umbraco 17 on GreenStack with GitHub

Welcome to GreenStack! This guide will walk you through everything you need to deploy your Umbraco 17 site to our Docker-based hosting platform. Whether you’re migrating an existing site or starting fresh, we’ve made the process as straightforward as possible.

Before we begin, make sure you have:

  • An Umbraco 17 project ready for deployment
  • A GreenStack hosting account (sign up here)
  • Access to a GitHub repository
  • Basic familiarity with your CI/CD on GitHub

If you find this setup overwhelming or too complicated, get in touch with us as we can handle the whole setup process for you!

GreenStack uses Docker Swarm to host containers behind a Traefik reverse proxy and Cloudflare Tunnel to serve your Umbraco sites. This means your application runs in an isolated container environment, with SSL termination handled automatically at the proxy level in the way officially supported by Umbraco.

This architecture requires a few small adjustments to your Umbraco project, which we’ll cover in detail below.


GreenStack handles SSL/TLS at the proxy level, which means your container receives traffic over HTTP internally. Umbraco needs to be configured to trust the forwarded headers from the proxy and to work correctly in this environment.

Open your Program.cs file and configure the forwarded headers middleware. This tells ASP.NET Core to trust the X-Forwarded-For and X-Forwarded-Proto headers from Traefik.

Why is this needed? Without this configuration, Umbraco would see all requests as coming from the internal Docker network over HTTP, which could cause issues with URL generation, security features, and accessing the backoffice.

Umbraco includes a health check that verifies HTTPS is configured. Since GreenStack handles SSL at the proxy level, we need to disable this check to ensure access to the Umbraco backoffice works correctly.

Add a HealthChecksDisabledChecks entry to your appsettings.json, nested inside your existing UmbracoCMS section. This disables the HTTPS/HSTS health check by its ID.

The check ID differs between Umbraco versions, so copy the correct appsettings.json for your version (you can find the most up to date file in the GitHub repository here).

When running in Production mode, Umbraco validates that HTTPS is configured at the application level. We need to remove this validator since SSL is handled externally.

Create a new file called DockerChecksRemoverComposer.cs in your project (you can find the most up to date file in the GitHub repository here).

This composer runs automatically when your application starts and removes the HTTPS validator from the runtime checks.

1.4 Exclude UmbPanel Domains from www Redirects

Section titled “1.4 Exclude UmbPanel Domains from www Redirects”

If your site enforces a www → non-www redirect (or vice versa), you must exclude all *.umbpanel.io domains from that redirect. GreenStack serves your site — including the back office and the live preview — over *.umbpanel.io URLs, so a redirect that rewrites the host will break access to your site through those URLs.

It also breaks the load balancer: GreenStack’s health checks reach your container over its *.umbpanel.io domain, and a redirect response (rather than a 200) causes the check to fail and your container to be pulled out of rotation — taking the site down.

GreenStack runs your site on Kestrel, not IIS, so there is no web.config. There are two supported ways to set up the redirect — in both cases the goal is to ensure *.umbpanel.io is never redirected.

Umbraco can load IIS-style rewrite rules from an iisrewrite.xml file via the URL Rewrite middleware (see the Umbraco URL Rewrites documentation). Add a negate condition so umbpanel.io hosts are skipped:

<?xml version="1.0" encoding="utf-8" ?>
<rewrite>
<rules>
<rule name="Redirect www to non-www" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{HTTP_HOST}" pattern="^www\.(.+)$" />
<!-- Don't redirect UmbPanel domains -->
<add input="{HTTP_HOST}" pattern="umbpanel\.io$" negate="true" />
</conditions>
<action type="Redirect" url="https://{C:1}/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>

Register it in Program.cs:

using Microsoft.AspNetCore.Rewrite;
var rewriteOptions = new RewriteOptions()
.AddIISUrlRewrite(builder.Environment.ContentRootFileProvider, "iisrewrite.xml");
app.UseRewriter(rewriteOptions);
// On Linux, UseStaticFiles() must come AFTER the UseUmbraco() statements
// for the rewrites to take effect.
app.UseStaticFiles();

Make sure the file is copied to the build output in your .csproj:

<ItemGroup>
<Content Include="iisrewrite.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

Option 2 — Built-in redirect middleware in Program.cs

Section titled “Option 2 — Built-in redirect middleware in Program.cs”

The ASP.NET Core rewrite middleware has built-in www redirect helpers. Pass only your production host(s) to the domains parameter — the rule then fires only for those hosts, so *.umbpanel.io is never matched and never redirected:

using Microsoft.AspNetCore.Rewrite;
// Redirect www → non-www for your production host only.
// UmbPanel domains aren't listed, so preview URLs and health checks keep working.
var rewriteOptions = new RewriteOptions()
.AddRedirectToNonWwwPermanent("www.example.com");
app.UseRewriter(rewriteOptions);
// On Linux, UseStaticFiles() must come AFTER the UseUmbraco() statements.
app.UseStaticFiles();

Note: The examples show a www → non-www redirect. For the opposite direction use AddRedirectToWwwPermanent and list your bare domain (e.g. example.com). Either way, *.umbpanel.io must never be redirected.

GreenStack containers are recreated on each deployment, so by default your .NET Data Protection keys would be regenerated each time — invalidating authentication cookies, antiforgery tokens, and anything else protected by them. Add a DataProtectionComposer.cs that persists the keys to disk so they survive restarts and deployments.

Create a new file called DataProtectionComposer.cs in your project (you can find the most up to date file in the GitHub repository here). See How to persist .NET Data Protection keys for the full walkthrough.

1.6 Configure a SignalR Backplane (only if running 2+ replicas)

Section titled “1.6 Configure a SignalR Backplane (only if running 2+ replicas)”

If you scale your GreenStack site to 2 or more replicas, the Umbraco back office needs a shared SignalR backplane so editors connected to different replicas stay in sync. Follow the Using existing infrastructure approach from the Umbraco documentation: SignalR in Backoffice Load Balanced Environment — Using existing infrastructure. See How do I configure SignalR when running multiple GreenStack replicas? for context on when this applies.

Note: This step is not required if you’re running a single replica — a SignalR backplane only needs configuring once you scale beyond one container.


GreenStack uses Docker to containerise and deploy your application. You’ll need a Dockerfile in the root of your repository.

Create a file named Dockerfile (no extension) (you can find the most up to date files in the GitHub repository here).

Important: Replace WEB_PROJECT_FOLDER/WEB_PROJECT_NAME.csproj with the folder and csproj file name of your Umbraco project. You will also need to replace WEB_PROJECT_NAME.dll with the name of your web project’s DLL (typically this is the same as your web project csproj file).

To speed up builds and reduce image size, create a .dockerignore file in your repository root (you can find the most up to date files in the GitHub repository here).


GreenStack integrates with GitHub Actions to automatically build and deploy your site when you push changes. We provide ready-made templates for GitHub Actions.

Create the following directory structure in your repository:

.github/
workflows/
deploy.yml

Create .github/workflows/main.yml (you can find the most up to date files in the GitHub repository here).

Important: Replace GITHUB_ACCOUNT_NAME/DOCKER_IMAGE_NAME with the name of your Docker image.


Access your control panel at www.umbpanel.io and navigate to your GreenStack service.

In GitHub go to Settings → Developer settings → Personal access tokens → Tokens (classic). On this page click on the Generate new token (classic) button.

  • Enter a descriptive name in the Note field
  • Set an expiry date for the token
  • Select read:packages scope
  • Click Generate token

Make a note of the generated token as you cannot see it again!

In UmbPanel enter the requested registry details in the form:

Field NameDescription
NameYour registry name, this is typically the username for your account in lowercase
UsernameYour GitHub username (the one shown on your GitHub profile / used to log in) — not the email address associated with your GitHub account. This must match the user that generated the personal access token.
Access TokenThe token we previously generated
Organisation NameIf the repository is stored in a GitHub Organisation enter the name here

Click Save to continue onto the next step.

Once you’ve saved the registry above, click Manage Websites. During first-time setup — after the repository/registry has been configured — you’ll be prompted to enter the Docker image name (this was entered in the main.yml file earlier). This is a one-time step that only appears when the site is first set up. The name should be all lowercase and minus any labels.

Important: Enter only the image name itself — do not include the registry host, your GitHub username, or your organisation name. Those have already been captured in the registry step above.

For example, if your full image reference in main.yml is:

ghcr.io/umbhost/my-umbraco-site:latest

then the value you enter here is just:

my-umbraco-site

…not umbhost/my-umbraco-site and not ghcr.io/umbhost/my-umbraco-site.

Important: GreenStack looks to the label called latest automatically.

In your GitHub repository, go to Settings → Secrets and variables → Actions and add the following secret:

Secret NameDescription
WEBHOOK_ENDPOINTYour deployment webhook URL

You can find the webhook value in the UmbPanel control panel.

Heads up: the webhook URL isn’t generated until after your initial deployment has run, so the first push to main is expected to fail at the trigger step. Once that first build has completed and the image has been pulled by GreenStack, head back to UmbPanel to grab the webhook URL, add it as the WEBHOOK_ENDPOINT secret, and re-run the workflow (or push another commit) — subsequent deployments will then complete end-to-end.


Once everything is configured:

  1. Commit and push your changes to the main branch
  2. GitHub Actions will automatically build your Docker image
  3. The image is pushed to your GitHub registry
  4. The webhook will trigger the deployment

You can monitor the deployment progress in both GitHub Actions, and you can see the task status in UmbPanel once deployed.


My first GitHub Actions run failed at the webhook step

Section titled “My first GitHub Actions run failed at the webhook step”

This is expected. The deployment webhook URL is only generated after your first deployment to GreenStack, so the WEBHOOK_ENDPOINT secret can’t exist on the very first push. The image will still build and push to your registry — only the final trigger step will fail. Once the initial deployment has been completed in UmbPanel, copy the webhook URL from your service, add it as the WEBHOOK_ENDPOINT repository secret, and re-run the workflow. From then on, every push will deploy automatically.

”HTTPS is required” when trying to access the backoffice

Section titled “”HTTPS is required” when trying to access the backoffice”

Make sure you’ve completed all three configuration steps in Step 1. The health check, runtime validator, and forwarded headers all need to be configured correctly.

”Invalid redirecturi” when trying to access the backoffice

Section titled “”Invalid redirecturi” when trying to access the backoffice”

Make sure the environment variable called Umbraco__CMS__Security__BackOfficeHost contains the umbpanel.io preview URL for your instance. After your site has gone live you may need to update this variable to contain your site’s actual URL.

Verify that the project path in your Dockerfile matches your actual project structure. The path is case-sensitive on Linux.

Views or partials aren’t found after deployment

Section titled “Views or partials aren’t found after deployment”

Linux containers are case-sensitive, unlike Windows. A partial referenced as @await Html.PartialAsync("MyPartial") won’t be found if the file is actually named myPartial.cshtml. Make sure the casing of every view, partial, and folder name referenced in your code matches the casing on disk exactly.

My custom dashboards / property editors are missing after deployment

Section titled “My custom dashboards / property editors are missing after deployment”

Anything in App_Plugins (custom dashboards, property editors, and other back-office extensions) must end up in the published output that’s copied into the Docker image. If these are missing after your first successful deployment:

  • Confirm the App_Plugins files are committed to your repository.
  • Make sure they aren’t excluded by your .dockerignore file.
  • Ensure they’re copied to the build output — for example, in your .csproj:
<ItemGroup>
<Content Include="App_Plugins\**\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

My site is down, unhealthy, or redirects away when accessed via the umbpanel.io URL

Section titled “My site is down, unhealthy, or redirects away when accessed via the umbpanel.io URL”

This is usually caused by a www redirect rule that doesn’t exclude UmbPanel domains. As well as breaking access via the preview URL, the redirect makes GreenStack’s load balancer health checks fail, so your container is pulled out of rotation and the site goes down. All *.umbpanel.io hosts must be excluded from any host-based redirect — see Step 1.4 above, or the dedicated guide How do I exclude UmbPanel domains from www redirects?, for IIS rewrite and Program.cs examples.



If you run into any issues or have questions, we’re here to help! You can:

If you find this setup overwhelming or too complicated, get in touch with us as we can handle the whole setup process for you!