Ranjan Marathe Blog

Running Docker on Windows 11

The Ask

Running Docker on Windows 11 without Docker Desktop, WSL, or Docker Engine (since it’s not free on Windows Server versions). And if you feel particularly ambitious, let’s also make it work across other Windows and Linux flavors.

The challenge is clear: how do you install and run Docker containers natively on a Windows environment without falling back on typical tools like Docker Desktop or the Windows Subsystem for Linux (WSL)?


Step 1: Setting the Scene with PowerShell

PowerShell is clutch here. Let’s walk through the Docker installation process using Microsoft’s container support and PowerShell commands.

```bash docker –version

If Docker is installed, this will give you a version output. If not, it’s time to get your hands dirty.

Step 2: Installing Docker You can follow Microsoft’s guide to get Docker set up without Docker Desktop. Here’s the key part of the script for installing Docker on Windows, including enabling Hyper-V and containers:

powershell Copy Edit Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -All Enable-WindowsOptionalFeature -Online -FeatureName Containers -All Restart your system to apply the changes. Then:

bash Copy Edit Install-Package -Name docker -ProviderName DockerMsftProvider Once installed, check if the daemon is running:

bash Copy Edit docker ps At this point, Docker is running on Windows 11 without Docker Desktop or WSL.

Step 3: Building Docker Containers on Windows Server Windows containers need specific base images. For example:

dockerfile Copy Edit

Use the Windows Server Core base image

FROM mcr.microsoft.com/windows/servercore:ltsc2022

Set the working directory

WORKDIR /app

Download Python installer

ADD https://www.python.org/ftp/python/3.10.8/python-3.10.8-amd64.exe .

Install Python silently

RUN python-3.10.8-amd64.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0

Clean up

RUN del python-3.10.8-amd64.exe

Run Python script

CMD [“python”, “your_script.py”] Step 4: Troubleshooting Common Issues SSL Certificate Errors Use pip’s trusted-host config:

bash Copy Edit pip config set global.trusted-host pypi.org files.pythonhosted.org pypi.python.org Unsupported Daemon Requests If you see errors like hcs::CreateComputeSystem, confirm that Hyper-V is properly enabled.

Step 5: Integrating SQLite with Your FastAPI App SQLite is lightweight and includes the sqlite3 module in Python’s standard library.

Dockerfile Example dockerfile Copy Edit

Use the Windows Server Core base image

FROM mcr.microsoft.com/windows/servercore:ltsc2022

Set the working directory

WORKDIR /app

Download Python installer

ADD https://www.python.org/ftp/python/3.10.8/python-3.10.8-amd64.exe .

Install Python silently

RUN python-3.10.8-amd64.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0

Remove the installer

RUN del python-3.10.8-amd64.exe

Set pip trusted hosts (important for avoiding SSL errors)

RUN pip config set global.trusted-host “pypi.org files.pythonhosted.org pypi.python.org” –trusted-host=pypi.python.org –trusted-host=pypi.org –trusted-host=files.pythonhosted.org

Copy the requirements file into the container

COPY requirements.txt .

Install dependencies

RUN pip install –no-cache-dir -r requirements.txt

Copy the application code into the container

COPY app/ ./app/

Copy the database folder into the container

COPY db/ ./db/

Expose port 8000

EXPOSE 8000

Command to run the FastAPI app using uvicorn

CMD [“uvicorn”, “app.search_api:app”, “–host”, “0.0.0.0”, “–port”, “8000”] FastAPI Code with SQLite Integration python Copy Edit import os import sqlite3 from fastapi import FastAPI, HTTPException, Query, Body from typing import List, Optional, Dict, Any from pydantic import BaseModel, model_validator

app = FastAPI()

Database configuration

BASE_DIR = os.path.dirname(os.path.abspath(file)) database_directory = os.path.join(BASE_DIR, ‘../db’)

if not os.path.exists(database_directory): os.makedirs(database_directory)

database_name = ‘sample_database.db’ database_path = os.path.join(database_directory, database_name) table_name = ‘sample_table’

Initialize SQLite database

def initialize_database(): conn = sqlite3.connect(database_path) cursor = conn.cursor() cursor.execute(f”SELECT name FROM sqlite_master WHERE type=’table’ AND name=’{table_name}’;”) if not cursor.fetchone(): cursor.execute(f’’’ CREATE TABLE {table_name} ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, email TEXT ) ‘’’) conn.commit() print(f”Table ‘{table_name}’ created.”) conn.close()

Fetch allowed columns from SQLite database

def get_allowed_columns(): conn = sqlite3.connect(database_path) cursor = conn.cursor() cursor.execute(f’PRAGMA table_info(“{table_name}”)’) columns_info = cursor.fetchall() conn.close() return [column[1] for column in columns_info]

@app.on_event(“startup”) def startup_event(): initialize_database() global allowed_columns allowed_columns = get_allowed_columns()

class Record(BaseModel): data: Dict[str, Any]

@model_validator(mode='after')
def validate_columns(cls, values):
    data = values.data
    invalid_columns = [col for col in data.keys() if col not in allowed_columns]
    if invalid_columns:
        raise ValueError(f"Invalid columns: {', '.join(invalid_columns)}")
    return values

@app.post(“/records”, response_model=Record) def create_record(record: Record): data = record.data columns = ‘, ‘.join(f’“{col}”’ for col in data.keys()) placeholders = ‘, ‘.join(‘?’ for _ in data) values = tuple(data.values())

conn = sqlite3.connect(database_path)
cursor = conn.cursor()
query = f'INSERT INTO "{table_name}" ({columns}) VALUES ({placeholders})'
try:
    cursor.execute(query, values)
    conn.commit()
except sqlite3.Error as e:
    conn.close()
    raise HTTPException(status_code=500, detail=f"Database error: {e}")
conn.close()

return record

@app.get(“/records”, response_model=List[Record]) def read_records( column: Optional[str] = Query(None, description=”The column name to filter”), value: Optional[str] = Query(None, description=”The value to filter by”) ): conn = sqlite3.connect(database_path) cursor = conn.cursor()

if column and value:
    if column not in allowed_columns:
        conn.close()
        raise HTTPException(status_code=400, detail="Invalid column name.")
    query = f'SELECT * FROM "{table_name}" WHERE "{column}" = ?'
    cursor.execute(query, (value,))
else:
    query = f'SELECT * FROM "{table_name}"'
    cursor.execute(query)

rows = cursor.fetchall()
conn.close()

if not rows:
    raise HTTPException(status_code=404, detail="No records found.")

column_names = allowed_columns
results = []
for row in rows:
    row_dict = dict(zip(column_names, row))
    results.append(Record(data=row_dict))

return results

@app.get(“/health”) def health_check(): return {“status”: “OK”}

@app.get(“/tables”) def list_tables(): conn = sqlite3.connect(database_path) cursor = conn.cursor() cursor.execute(“SELECT name FROM sqlite_master WHERE type=’table’;”) tables = cursor.fetchall()

if not tables:
    return {"message": "No tables found"}

table_details = {}
for table in tables:
    table_name = table[0]
    cursor.execute(f"PRAGMA table_info('{table_name}')")
    columns = cursor.fetchall()
    table_details[table_name] = [{"name": col[1], "type": col[2]} for col in columns]

conn.close()
return table_details Testing SQLite with Docker Example cURL commands:

Create a record

bash Copy Edit curl -X POST “http://localhost:8000/records”
-H “accept: application/json”
-H “Content-Type: application/json”
-d ‘{“data”: {“name”: “John”, “age”: 30, “email”: “john@example.com”}}’ Read all records

bash Copy Edit curl -X GET “http://localhost:8000/records”
-H “accept: application/json” List tables

bash Copy Edit curl -X GET “http://localhost:8000/tables”
-H “accept: application/json” Wrapping Up By rolling with SQLite, you’ve got a compact setup that’s easy to manage in Docker. This method outlines building, running, and testing an app on Windows 11 without Docker Desktop or WSL—no screaming needed.