Setting Up Windows Containers in Docker/Kubernetes

Windows containers behave differently from Linux containers and fail where everything has already been tested. We configure Windows containers in a cluster on a turnkey basis: we select the isolation mode, prepare hosts, and define update policies. Downtime during service updates is reduced, and the team gets a predictable environment.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Why Windows containers fail on a foreign host

The image is built on a workstation, starts locally, and serves a page — but on the server the container fails at startup or dies on the first request.

The first things checked are resources, environment variables, and ports, but more often the cause is deeper: the image and the host diverge by base branch and isolation model, and the image names do not reveal this.

Windows containers are tightly bound to the host version. An image built for one base branch on a node with another will either not start at all or start but without the required components inside.

The image tag is the same, however, so two builds from different branches sit side by side in the registry — and deployment succeeds on one machine but not on another.

Process isolation here is structured differently than in Linux. The container and host share a kernel, but permissions for the file system, registry, and network ports are inherited by different rules, and the mode with a separate kernel adds its own requirements to the node.

A scheme debugged under one mode falls apart when switching to another — and a startup failure looks random.

Until the cause is found, the release goes in circles: the orchestrator restarts the container, checks fail, the rollout stalls. Rollback does not help either if the previous image was built from the same wrong base — the team loses hours trying out hypotheses. We'll break down how this is resolved.

What a managed container environment delivers

The effect of containers is measured not by how “trendy” the approach is, but by four numbers: build time, update window length, downtime during deployment, and the share of failures caused by the environment. Below is how each changes.

Predictable build

The image is built layer by layer: when code is edited, unchanged layers are reused, and the build does not pull the entire set of dependencies again.

Build time drops from tens of minutes to just a few, and the result is identical on the developer's machine, in the test environment, and in production.

Short update window

Deployment proceeds replica by replica: the new version comes up, passes readiness checks, and only then does traffic move off the previous one. The update window shrinks to the time of a single replica rather than a full service outage.

Service downtime — seconds, not nightly windows

Rollback is replacing the image with the previous one, not restoring a server from a snapshot. The same mechanism covers planned maintenance: updates no longer require a coordinated nightly window and an on-call engineer watching a monitor.

Environment repeatability

The environment is described declaratively once, so configuration drift between environments ceases to be a cause of failure. The class of failures “it worked in test, not in production” stops being regular — what remains are logic errors caught by checks before deployment.

Windows containers: isolation modes and compatibility

The isolation mode addresses compatibility, not speed. In process mode, containers share the host kernel: startup is faster, packing density is higher, but the host kernel version must match the base image line — otherwise the container will not come up at all.

Hypervisor mode gives each container a separate kernel and removes this requirement: workloads for different image lines coexist on one host.

The cost is heavier startup, lower density, and mandatory nested virtualization if the nodes in Kubernetes are themselves running on virtual machines.

Option When it fits Mode limitations
Process mode, shared kernel Homogeneous host fleet, dense packing, long-running services Host kernel version must match the image line; no kernel isolation
Hypervisor mode Heterogeneous hosts, multi-tenancy, legacy workloads alongside new ones Heavier startup, lower density, nested virtualization required
Separate pool of Windows nodes Mixed cluster, Linux workloads alongside Windows Scheduling by labels and taints, some network plugins unavailable
Pool for a single base image line Strict version compatibility requirements, infrequent updates More pools, host updates synchronized with images

Implementation sequence: from host audit to handover

We start with a survey, not installation: Windows containers are sensitive to matching system versions across nodes, support for the required isolation mode, and a shared base image layer.

Skip this step and the cluster will come together but fall apart at the first scheduled image update.

  1. Infrastructure audit. Inventory of the machine fleet: OS branch, availability of the isolation mode, storage drivers, routes to the image registry. Result: a “node — suitability — what needs finishing” table.

  2. Environment design. We lay out the environments, calculate resources for system and workload containers, and decide where images are built and where they are only pulled. We agree on it before configuration so the network does not have to be reworked later.

  3. Cluster setup. Control plane, a separate node pool for Windows, placement rules, and resource limits: system processes stop competing with workload containers.

  4. Base images and registry. A single parent image, tag versioning, retention and cleanup policy — the registry does not grow endlessly, and rollback to the previous release remains possible.

  5. Load testing. A profile close to production: peak startup, base image update, node failure. We look at container startup time and recovery after failure.

  6. Handover to the team. Configurations, environment design, update procedures, and a procedure for diagnosing typical failures; a walkthrough with the on-call shift.

Each stage closes with a verifiable artifact, so the output is a working environment and a team that can evolve it without us.

Deliverable set of configurations, diagrams, and procedures

The work ends not with a running environment but with a set of artifacts that can be used to rebuild it from scratch, extend it, and hand it over to another team.

Everything lives in the repository alongside the applications: versioned, checked, and changed together with the code.

  • Environment configuration — declarative workload descriptions, isolation parameters and resource limits, host version requirements for a specific image. The environment is brought up from these files without manual edits.
  • Network diagram — subnets and routes between nodes, exit points into the corporate environment, address translation rules. With notes on which ports are open and why.
  • Access policies — roles and bindings for teams, service accounts, prohibition of privileged workloads, permissions for the image registry and secret store.
  • Update procedure — order of rolling out new versions of base images and hosts, maintenance windows, criteria for rolling back to the previous set, actions for version incompatibility.
  • Maintenance procedure — collected metrics and alert thresholds, log collection order, who responds and within what timeframe.
  • Documentation and handover — step-by-step deployment from scratch, acceptance checklist, description of known limitations, configuration walkthrough with your team.

The set remains with you unchanged: from then on, your team makes edits itself without contacting us for every change.

Case study: eliminated downtime during service updates

A fleet of two dozen internal services lived on separate virtual machines: updating a version meant a nightly window, stopping the service, and manual checks after startup.

One service took from half an hour to an hour, and a failed release was rolled back manually — returning the previous build took several hours and required an on-call engineer at the console.

What we changed

The services were moved into containers managed by Kubernetes, with a separate pool of Windows nodes allocated.

Configuration and secrets were moved out of images into standalone objects, tags were pinned instead of using the floating latest, and readiness and liveness probes were described for each service: traffic reaches a container only after the application actually responds.

Updates were switched to a rolling replacement scheme — new containers come up alongside the old ones and take traffic while the previous ones are still running.

Rollback became a return to the previous image tag: the previous version comes up from the registry in minutes, without rebuilding or manual operations on the server.

What changed

Downtime during service updates has disappeared completely — releases happen during working hours, and users do not notice the switch.

Returning to the previous version takes just a few minutes instead of several hours, and the share of updates completed without engineer intervention grew from about 70% to 98%.

Images became almost three times lighter after shared dependencies were moved into the base layer — build and delivery to the node accelerated accordingly. Service recovery after a node failure takes about five minutes: the cluster itself moves containers to available resources.

What to do if containers are already deployed in the cluster?

An already deployed cluster built ad hoc is not a reason to stop services for a “beautiful” migration. The services are running, but no one can say exactly which image a specific instance was built from or what was edited manually in the live environment.

The work order is as follows: survey, stabilization, then handover under procedure — and no step requires a downtime window.

We take inventory from the cluster itself, not from someone else’s documentation: the list of workloads, their owners, repository binding, floating image tags, places without resource limits and liveness probes.

For Windows containers, we separately verify which nodes they run on and whether the host environment version matches — otherwise the very first migration will run into exactly this mismatch.

Stabilizing the environment starts with the most dangerous spots: pinning image versions instead of floating tags, resource limits and readiness checks, spreading critical services across multiple nodes.

We make changes one at a time and observe behavior — otherwise it is impossible to tell which change had an effect and which merely coincided in time.

Zero-downtime migration proceeds through a parallel environment: the target environment comes up alongside, and traffic is switched in parts — first one non-critical service, then groups by dependency.

For each group, the reverse switch is described in advance, and readiness is checked before the switch, not after.

From then on, the environment lives declaratively: changes only through files in the repository, manual edits in the live cluster are prohibited, and drift between the actual state and the description is detected automatically.

This way technical debt stops growing, and workload inventory turns from a one-off campaign into a regular reconciliation — and the customer gets a predictable handover of the environment into operations.

Common technical doubts before starting

Windows containers do not move between machines as freely as Linux images: a container shares the kernel with the host, so compatibility is determined not only by the image itself but also by the isolation mode of the receiving node.

We record these points in a compatibility matrix and verify them in the build pipeline rather than discovering them at release time. Below are the doubts that most often come up before the first rollout.

Will the container start on any node in the cluster?

No. The image and the node must be on the same branch — a mismatch causes startup failure or unstable operation under load.

We pin the branch directly in the base image tag, run the “image — node” pair through the pipeline, and assign labels to nodes so the scheduler does not place a task on an incompatible host.

What permissions does the process inside the container need?

The process runs under an unprivileged account, so access to network directories, certificates, and volumes is granted explicitly — through a managed group account or a separate service account.

The deliverable includes a description: which account, which permissions, and exactly what will stop working without them.

Can the base image be updated in a running container?

No: an update over the network will be lost the first time the instance is recreated. Updates happen only by rebuilding with a new base layer tag, running checks, and rolling out.

We keep the previous tag in the registry, so rollback means returning to it, not restoring from scratch.

What changes when moving to another host or environment?

The image moves, but operability depends on the node kernel branch and isolation mode. In a cluster, we keep nodes on one branch, and when moving to another environment, we check the matrix and run the acceptance test suite on the new host.

What about licensing during migration?

A container does not cancel licenses for either the OS or the workloads inside. During migration, we make sure the receiving machine is covered; the placement scheme and coverage list are included in the deliverable so the issue does not come up during an audit.

Let's discuss the task and come back with an estimate and plan

No access to the production environment is needed for an estimate.

Input data is enough: where the containers live — a self-hosted Kubernetes installation or a provider’s managed service, which Windows host edition is used, which applications are being migrated, and whether they need a graphical interface.

This is sufficient to understand the scope of work and compatibility risks rather than discovering them during implementation.

After discussing the task, a stage estimate follows — not a “let’s try and see,” but a work plan broken down by blocks:

  • node placement scheme: a shared group or separate pools for Linux and Windows workloads, taking into account how each group is updated;
  • choice of base images — a minimal edition without a graphical shell or a more complete one, with an estimate of size and update frequency;
  • isolation mode and compatibility with the host edition, so the image starts on all nodes without selective exceptions;
  • image building and registry storage: tagging rules, cleanup of old layers, preserving the ability to roll back to the previous release;
  • secrets, volumes, and network policies — as configuration files suitable for reapplication, not manual edits;
  • verification order: deployment to the test environment, fault-tolerance testing, criteria for moving to production.

The first stage begins with a survey and migration of one service — it shows how containers behave under load and the cost of maintenance. Then we replicate the proven scheme to the remaining applications, without redesigning the solutions.

Describe the task: the application inventory, update window requirements, and who is responsible for infrastructure on your side. We will come back with an estimate, a work plan, and the boundaries of the first stage.