Showing posts with label Docker. Show all posts
Showing posts with label Docker. Show all posts

Tuesday, September 29, 2026

docker-compose with MySQL and ASP.NET

This article discussed one approach to having your ASP.NET development environment work with MySQL running in a docker container.

Source code: https://github.com/medhatelmasry/AspMySQL

It is assumed that the following installed on your computer:

  1. .NET 10.0 
  2. Docker Desktop 
  3. ‘dotnet-ef’ tool 

Setting up MySQL docker container

To download a suitable MySQL image from Docker Hub and run it on your local computer, type the following command from within a terminal window:

docker run -d --name mysqldb -p 3333:3306 -e MYSQL_ROOT_PASSWORD=secret mysql:8.4

This starts a container named mysqldb that listens on port 3333 on your local computer. The root password is secret.

To ensure that the MySQL container is running, type the following from within a terminal window:

docker ps

You will see a message like the following:

CONTAINER ID   IMAGE       COMMAND                  CREATED       STATUS          PORTS                                         NAMES
4baa0ba99088   mysql:8.4   "docker-entrypoint.s…"   2 hours ago   Up 48 minutes   0.0.0.0:3333->3306/tcp, [::]:3333->3306/tcp   mysqldb

Creating our ASP.NET MVC App

Create an ASP.NET MVC app named AspMySQL with SQLite support by running the following terminal window commands:

dotnet new mvc --auth individual -o AspMySQL
cd AspMySQL

To run the web application and see what it looks like, enter the following command:

dotnet watch

The app starts in your default browser and looks like this:

We need a suitable MySQL Entity Framework driver. One such driver is the official MySQL driver from Oracle. Run this pair of commands to replace the SQLite driver with the Oracle MySQL driver:

dotnet remove package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package MySql.EntityFrameworkCore

Let us configure our web application so that the connection string can be constructed from environment variables. Open the Program.cs file in your favourite editor and comment out (or delete) the following statements around lines 8-10:

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));

Replace the above code with the following:

var host = builder.Configuration["DBHOST"] ?? "localhost";
var port = builder.Configuration["DBPORT"] ?? "3333";
var password = builder.Configuration["DBPASSWORD"] ?? "secret";
var db = builder.Configuration["DBNAME"] ?? "aspnetDB";
var user = builder.Configuration["DBUSER"] ?? "root";

string connectionString = $"Server={host};Port={port};Database={db};User={user};Password={password};";

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseMySQL(connectionString));

Five environment variables are used in the database connection string. These are: DBHOST, DBPORT , DBPASSWORD, DBNAME and DBUSER. If these environment variables are not found then they will take on default values: localhost, 3333, secret, aspnetDB and root respectively.

Go ahead and delete the connection string from appsettings.json as it is not needed anymore:

"ConnectionStrings": {
  "DefaultConnection": "DataSource=app.db;Cache=Shared"
},

Entity Framework Migrations

We can instruct our application to automatically process any outstanding Entity Framework migrations. This is done by adding the following statement to Program.cs right before the last app.Run() statement:

using (var scope = app.Services.CreateScope()) {
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<ApplicationDbContext>();    
    context.Database.Migrate();
}

Since SQLite is different from MySQL, we must delete all migrations and re-create them. Therefore, follow these steps:

  1. delete the Data/Migrations folder.
  2. delete file: app.db
  3. create new migrations with this terminal window command: 

dotnet ef migrations add M1 -o Data/Migrations

Test app

Now, let's test our web app and see whether it can talk to the containerized MySQL database server. Run the web application with the following terminal command:

dotnet watch

Click on the Register link on the top right side.

The ASP.NET MVC user register page.

I entered an Email, Password and Confirm password, then clicked on the Register button. The website then displays the following page that requires that you confirm the email address:

Click on the “Click here to confirm your account” link. This leads you to a confirmation page:

Login with the email address and password that you registered with.

The message on the top right side confirms that the user was saved and that communication between the ASP.NET app and MySQL is working as expected.

Dockeri-zing app

We will generate the release version of the application by executing the following command from a terminal window in the root directory of the web app:

dotnet publish -o distrib

The above command instructs dotnet to produce the release version of the application in the distrib directory. When you inspect the distrib directory, you will see files like the following:

The highlighted file in the above image is the main DLL file that is the entry point into the web application. Let us run the DLL. To do this, change to the distrib directory, then run your main DLL file with:

cd distrib
dotnet AspMySQL.dll

This displays the familiar messages from the web server that the app is ready to be accessed from a browser. 

Hit CTRL C to stop the web server.

We now have a good idea about the ASP.NET artifacts that need to be copied into a container. 

In a terminal window, stop and remove the MySQL container with:

docker rm -f mysqldb

Return to the root directory of your project by typing the following in a terminal window:

cd ..

Docker image for web app

We need to create a docker image that will contain the .NET runtime. At the time of writing this article, the current version of .NET is 10.0.

We can exclude files from being copied into the container image. Add a file named .dockerignore in the root of the web application with this content:

**/.git
**/.gitignore
**/node_modules
**/npm-debug.log
**/.DS_Store
**/bin
**/obj
**/distrib **/.vs **/.vscode **/.env **/*.user **/*.suo **/.idea **/coverage **/.nyc_output **/docker-compose*.yml **/Dockerfile* **/.github **/README.md **/LICENSE

Create a text file named Dockerfile and add to it the following content:

# Build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build

WORKDIR /src

# Copy project file and restore dependencies
COPY *.csproj .
RUN dotnet restore

# Copy source code
COPY . .

# Publish application
RUN dotnet publish -c Release -o /app/publish

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime

WORKDIR /app

COPY --from=build /app/publish .

ENV ASPNETCORE_URLS=http://+:80

EXPOSE 80
EXPOSE 443

# Run the application
ENTRYPOINT ["dotnet", "AspMySQL.dll"]

docker-compose.yml

We will next create a docker .yml file that orchestrates the entire system involving two containers: a MySQL database server and our web app. In the root folder of your application, create a text file named docker-compose.yml and add to it the following content:

volumes:
  mysqldata:

services:
  db:
    image: mysql:8.4
    volumes:
      - mysqldata:/var/lib/mysql
    #restart: always
    ports:
      - "3333:3306"
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_TCP_PORT: 3306
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-psecret" ]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 30s

  webapp:
    build:
      context: .
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "7777:80"
    #restart: always
    environment:
      - DBHOST=db
      - DBPORT=3306
      - DBPASSWORD=secret
      - DBNAME=snoopyDB
      - DBUSER=root

Running the yml file

To find out if this all works, go to a terminal window at the root directory of the application and run the following command:

docker-compose up -d --build

Point your browser to http://localhost:7777/ and you should see the main web page. Register a user, confirm the email, and login. It should all work as expected.

Cleanup

Run the following command to shutdown docker-compose and cleanup:

docker-compose down -v

Conclusion

We have seen how straight forward and easy it is to containerize an application and its database with docker-compose.

Thursday, February 12, 2026

docker-compose with SQL Server and ASP.NET

This article discussed one approach to having your ASP.NET development environment work with SQL Server (MSSQL) running in a docker container.

Source code: https://github.com/medhatelmasry/AspMSSQL

It is assumed that the following installed on your computer:

  1. .NET 10.0 
  2. Docker Desktop 
  3. ‘dotnet-ef’ tool 

Setting up SQL Server docker container

To download a suitable SQL Server image from Docker Hub and run it on your local computer, type the following command from within a terminal window:

docker run --cap-add SYS_PTRACE -e ACCEPT_EULA=1 -e MSSQL_SA_PASSWORD=SqlPassword! -p 1444:1433 --name mssql -d mcr.microsoft.com/mssql/server:2022-latest

This starts a container named mssql that listens on port 1444 on your local computer. The sa password is SqlPassword!.

To ensure that the SQL Server container is running, type the following from within a terminal window:

docker ps

You will see a message like the following:

CONTAINER ID   IMAGE                                        ...... NAMES
e84053717017   mcr.microsoft.com/mssql/server:2022-latest   ...... mssql

Creating our ASP.NET MVC App

Create an ASP.NET MVC app named AspMSSQL with SQL Server support by running the following terminal window commands:

dotnet new mvc --auth individual --use-local-db -o AspMSSQL
cd AspMSSQL

To run the web application and see what it looks like, enter the following command:

dotnet watch

The app starts in your default browser and looks like this:

The default page when starting an ASP.NET MVC application.

Let us configure our web application so that the connection string can be constructed from environment variables. Open the Program.cs file in your favourite editor and comment out (or delete) the following statements:

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

Replace the above code with the following:

var host = builder.Configuration["DBHOST"] ?? "localhost";
var port = builder.Configuration["DBPORT"] ?? "1444";
var password = builder.Configuration["DBPASSWORD"] ?? "SqlPassword!";
var db = builder.Configuration["DBNAME"] ?? "mydb";
var user = builder.Configuration["DBUSER"] ?? "sa";

string connectionString = $"Server={host},{port};Database={db};UID={user};PWD={password};TrustServerCertificate=True;";

Five environment variables are used in the database connection string. These are: DBHOST, DBPORT , DBPASSWORD, DBNAME and DBUSER. If these environment variables are not found then they will take on default values: localhost, 1444, SqlPassword!, mydb and sa respectively.

Go ahead and delete the connection string from appsettings.json as it is not needed anymore:

"ConnectionStrings": {
  "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-AspMSSQL; MultipleActiveResultSets=true"
},

Entity Framework Migrations

We can instruct our application to automatically process any outstanding Entity Framework migrations. This is done by adding the following statement to Program.cs right before the last app.Run() statement:

using (var scope = app.Services.CreateScope()) {
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<ApplicationDbContext>();    
    context.Database.Migrate();
}

Test app

Now, let's test our web app and see whether it can talk to the containerized MSSQL database server. Run the web application with the following terminal command:

dotnet watch

Click on the Register link on the top right side.

The ASP.NET MVC user register page.

I entered an Email, Password and Confirm password, then clicked on the Register button. The website then displays the following page that requires that you confirm the email address:

User Register Confirmation Page.

Click on the “Click here to confirm your account” link. This leads you to a confirmation page:

After a user confirms email, the user confirm email aler displays.

Login with the email address and password that you registered with.

The message on the top right side confirms that the user was saved and that communication between the ASP.NET MVC app and SQL Server is working as expected.

Dockeri-zing app

We will generate the release version of the application by executing the following command from a terminal window in the root directory of the web app:

dotnet publish -o distrib

The above command instructs dotnet to produce the release version of the application in the distrib directory. When you inspect the distrib directory, you will see files like the following:

A screen capture of the files in the bin folder containing the main DLL named AspMSSQL.dll

The highlighted file in the above image is the main DLL file that is the entry point into the web application. Let us run the DLL. To do this, change to the distrib directory, then run your main DLL file with:

cd distrib
dotnet AspMSSQL.dll

This displays the familiar messages from the web server that the app is ready to be accessed from a browser. 

Screen capture showing the terminal window after executing command "dotnet AspMSSQL.dll"

Hit CTRL C to stop the web server.

We now have a good idea about the ASP.NET artifacts that need to be copied into a container.

In a terminal window, stop and remove the MSSQL container with:

docker rm -f mssql

Return to the root directory of your project by typing the following in a terminal window:

cd ..

Docker image for web app

We need to create a docker image that will contain the .NET runtime. At the time of writing this article, the current version of .NET is 10.0.

We can exclude files from being copied into the container imag Add a file named .dockerignore in the root of the web application with this content:

**/.git
**/.gitignore
**/node_modules
**/npm-debug.log
**/.DS_Store
**/bin
**/obj
**/.vs
**/.vscode
**/.env
**/*.user
**/*.suo
**/.idea
**/coverage
**/.nyc_output
**/docker-compose*.yml
**/Dockerfile*
**/.github
**/README.md
**/LICENSE

Create a text file named Dockerfile and add to it the following content:

# Build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

# Copy project file and restore dependencies
COPY ["AspMSSQL.csproj", "."]
RUN dotnet restore "AspMSSQL.csproj"

# Copy the rest of the source code
COPY . .

# Build the application
RUN dotnet build "AspMSSQL.csproj" -c Release -o /app/build

# Publish stage
FROM build AS publish
RUN dotnet publish "AspMSSQL.csproj" -c Release -o /app/publish /p:UseAppHost=false

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app

# Install curl for health checks (optional)
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# Copy published application from publish stage
COPY --from=publish /app/publish .

# Expose port 8080 (HTTP)
EXPOSE 8080

# Set environment variables
ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production

# Run the application
ENTRYPOINT ["dotnet", "AspMSSQL.dll"]

docker-compose.yml

We will next create a docker yml file that orchestrates the entire system involving two containers: a MSSQL database server and our web app. In the root folder of your application, create a text file named docker-compose.yml and add to it the following content:

services:
  # SQL Server Service
  mssql:
    image: mcr.microsoft.com/mssql/server:2022-latest
    container_name: aspmsql-mssql
    environment:
      ACCEPT_EULA: 'Y'
      MSSQL_SA_PASSWORD: 'SqlPassword!123'
      MSSQL_PID: 'Developer'
    ports:
      - "1433:1433"
    volumes:
      - ./mssql-data:/var/opt/mssql/data

  # ASP.NET Application Service
  aspmsql-app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: aspmsql-app
    depends_on:
      - mssql
    environment:
      ASPNETCORE_ENVIRONMENT: Development
      ASPNETCORE_URLS: http://+:8080
      DBHOST: mssql
      DBPORT: 1433
      DBUSER: sa
      DBPASSWORD: SqlPassword!123
      DBNAME: AspMSSQLDb
    ports:
      - "8080:8080"
    restart: unless-stopped

volumes:
  sqlserver-data:
    driver: local

Running the yml file

To find out if this all works, go to a terminal window and run the following command:

docker-compose up -d --build

Point your browser to http://localhost:8080/ and you should see the main web page. Register a user, confirm the email, and login. It should all work as expected.

Screen capture showing that qq@qq.qq is logged into the web app.

Cleanup

Run the following command to shutdown docker-compose and cleanup:

docker-compose down

Conclusion

We have seen how straight forward and easy it is to containerize an application and its database with docker-compose.

Monday, October 6, 2025

Explore Docker MCP Toolkit and VS Code

To explore Docker MCP Toolkit, we will use two MCP server in the toolkit, namely PostgreSQL and Playwright.

Companion Video: https://youtu.be/43oJi_gAucU

What is Docker MCP Toolkit?

The Docker MCP Toolkit enables hosting and managing MCP servers. These servers expose APIs for specific development tasks, such as retrieving GitHub issue data or querying databases using natural language.

Prerequisites

You will need the following before you can continue:

  • Docker Desktop (latest version)
  • Visual Studio Code (latest version)
  • GitHub Copilot extension for VS Code
  • GitHub Copilot with Chat and Agent Mode enabled

1) Explore PostgreSQL MCP Server

We will use natural language to query a PostgreSQL database that is already pre-loaded with the sample Northwind database. To run the PostgreSQL server in a docker container on your computer, execute the following command from any terminal window:


docker run --name psqlnw -e POSTGRES_PASSWORD=VerySecret -p 5433:5432 -d melmasry/nw-psql:1.0.0

Start “Docker Desktop” on your computer and go to the Containers tab on the left navigation. You will see that the psqlnw container is running.


Next, let us use the Docker MCP Toolkit. In Docker Desktop, click on “MCP Toolkit” in the left navigation.

Click on the Catalog tab. This will show you a list of MCP Servers that are ready for you to explore. 

We will start with PostgreSQL. Find the PostgreSQL MCP Server by entering ‘postgr’ in the filter field. Then click on + to add it to your list.

You will be asked to enter a secret. This is nothing but the connection string and it is based on this format:

postgresql://readonly_user:readonly_password@host:port/database_name

In our case, this would be:

postgresql://postgres:VerySecret@host.docker.internal:5433/northwind

Click on the "My Servers" tab to see the MCP servers that you have chosen.

Connect Docker MCP Toolkit to Visual Studio Code

Let us query our northwind database in PostgreSQL from VS Code. Go to a working directory and execute these commands to create an empty folder on your computer:

mkdir mcp-server
cd mcp-server

In the same terminal window, logout and login into docker with:

docker logout  
docker login

Start VS Code in the folder with

code .

In VS Code, open the Command Palette by pressing Ctrl + Shift + P (or Cmd + Shift + P on macOS).

Select “Add MCP Server”.

Select “Command (stdio) Run a local command that implements the MCP protocol Manual Install”.

Enter the gateway command:

docker mcp gateway run

Give the server an ID named: 

my-mcp-server

Choose “Workspace Available in this workspace, runs locally”.


Click Trust.

A file named mcp.json is created in a .vscode folder with this content:

Note that the server is running. 

Inside the Github Copilot Chat window, choose Agent and any Claude model. Click on the tools icon to see active MCP servers.

You will find MCP servers that are configured in VS Code. Among them will be the one we enabled in the Docker MCP Toolkit.

You will notice that it only has one tool: “query Run a read-only SQL query”. This is all that is needed to query the northwind database.

Click ok the Ok button to close the tools popup.

Enter this prompt in the GitHub Copilot window:

What are the tables in the PostgreSQL northwind database?

You will be asked to click on the Allow button.

Thereafter, it displays a list of database tables in the northwind database.

Try this other prompt:

What are the products supplied by "Exotic Liquids"?

You will get a similar response to this:

2) Explore Playwright MCP Server

Back in Docker Desktop >> MCP Toolkit, add Playwright.

You now have two MCP servers in our list: Playwright and PostgreSQL.

Back in VS Code, restart the MCP Server in the .vscode/mcp.json file.

In the VS Code GitHub Copilot Chat window, enter this prompt:

Using the tools provided from the Docker MCP Server, navigate to https://www.bbc.com/, find the two most important business stories.

I got the following response at the time of writing this article:

Conclusion

I hope you found this article useful. These are early days of MCP servers. I am sure things will evolve much more in this very important space.

Friday, August 15, 2025

Using the timer MCP Server in Visual Studio Code

In a previous article, I discussed “Getting started with MCP Server in Visual Studio Code”. I decided to update this article because the process has become much easier with the latest version of VS Code.

In this tutorial, you will learn how to enable and use MCP servers in Visual Studio Code. The simple example we will use is the Timer MCP Server.

Companion Video: https://youtu.be/H14yMdHuyM4

What is MCP?

Model Context Protocol (MCP) is an open standard developed by Anthropic, the company behind Claude models. 

MCP is an open protocol that enables seamless integration between AI apps & agents and your tools & data sources.

Prerequisites

  • Visual Studio Code
  • GitHub account
  • Github Copilot Chat extension
  • Docker Desktop

NOTE:  This article uses Visual Studio Code version 1.103.1 as shown below:

Later versions of Visual Studio Code will likely have minor UI changes that may look different from the screen captures in this article.

Getting Started

We will setup an MCP Server that provides current time information. This will be done in a local workspace, as opposed to making it globally available in VS Code. Also, the MCP server will run in Docker.

Start Docker Desktop.

Create a working directory named time-mcp-server and open VS Code in that directory with:

mkdir time-mcp-server
cd time-mcp-server
code .

In VS Code, click on the GitHub Copilot Chat icon and select “Open Chat”.

In the chat window that opens, choose Agent mode and the latest Claude model. A tools button appears.

When you click on the tools button, a pop-up window will display the tools used by VS-Code GitHub Copilot Chat.

Click on OK to close the pop-up window.

In Visual Studio Code, click on the gear in the bottom-left, then choose settings:

In the filter field, type MCP.

Note that Discovery is enabled. These are early days, so some Mcp features are in Experimental mode.

Click on 'Edit in settings.json'. 

The file will open in the editor. Note that chat.mcp.discovery.enabled is set to true.

You can close the settings.json file in the editor.

Find a suitable MCP Server

Go to https://github.com/modelcontextprotocol/servers. Search for Time under Reference Servers.

Click on the Time link. Then fing Configuration >> Configure for Claude App >> Unsing docker.

Take note of the name of the docker image mcp/time.

Setup a timer MCP Server

In Visual Studio Code, click on View >> Command Palette….

Choose “MCP: Add Server…”.

Choose “Docker Image Install from a Docker image”.

For "Docker Image Name", enter “mcp/time”.

Next, choose Allow.

Accept the default Server ID.

Choose “Workspace Available in this workspace, runs locally”.

When prompted to trust and run the MCP server time, click on Trust.

A file named mcp.json gets created in a folder named .vscode inside your workspace with content:

{
"servers": {
"time": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"mcp/time"
],
"type": "stdio"
}
},
"inputs": []
}

If the MCP server is not yet started, click on Start.

This will start the mcp/time server in Docker.

In the GitHub Copilot Chat window, enter: What is the current time?

 VS Code detects the running MCP server which knows how to handle requests for current time:

Click on Continue and you will get a response like this:

You can ask it to convert the time to another time zone with: What is that in pacific time zone?

Again, it should resort to the time MCP server for assistance.

Click on Continue to get your answer.

You can even ask the time in another country. Example: What time is it in Cairo, Egypt?

Again, it uses our MCP time server:

Click on Continue.

Finally, let us ask it to refresh the current time, with: Refresh the time in Egypt.

Conclusion

The ecosystem for MCP servers is growing rapidly. There are already many servers that you can explore at https://github.com/modelcontextprotocol/servers.