Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .diploi/helm/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ spec:
- /bin/sh
- -c
- >
dotnet build || exit 1;
(dotnet restore && dotnet build) || exit 1;
(dotnet tool restore && dotnet tool run libman restore) || echo "Libman restore failed, continuing...";
workingDir: /app{{ .Values.folder }}
volumeMounts:
Expand Down Expand Up @@ -78,6 +78,10 @@ spec:
- name: {{ .identifier }}
value: {{ .value | quote }}
{{- end }}
{{- if eq .Values.stage "development" }}
- name: NUGET_PACKAGES
value: /app{{ .Values.folder }}/.nuget
{{- end }}
volumeMounts:
{{- if hasKey .Values.storage "code" }}
- name: app-mount
Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,10 @@ docker-compose.override.yaml
##

tmp/
temp/
temp/

# NuGet packages
packages/
*.nuget.props
*.nuget.targets
.nuget/
13 changes: 11 additions & 2 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
FROM mcr.microsoft.com/dotnet/sdk:10.0

RUN apt-get update \
&& apt-get install -y --no-install-recommends tini nodejs npm iproute2 net-tools \
&& npm install -g nodemon \
&& rm -rf /var/lib/apt/lists/*

ARG FOLDER=/app

COPY --chown=1000:1000 . /app
Expand All @@ -10,10 +15,14 @@ USER 1000:1000

EXPOSE 5054

ENV ASPNETCORE_URLS=http://+:5054
ENV ASPNETCORE_URLS=http://0.0.0.0:5054

ENV ASPNETCORE_ENVIRONMENT=Development

ENV ASPNETCORE_HTTPS_PORT=""

CMD ["dotnet", "watch", "--no-launch-profile", "--hot-reload", "--non-interactive"]
ENV DOTNET_USE_POLLING_FILE_WATCHER=1

ENTRYPOINT ["/usr/bin/tini", "--"]

CMD ["nodemon"]
15 changes: 15 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
using component_blazor.Components;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Server.Kestrel.Core;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();

builder.Services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds(1);
});

// Configure Kestrel to allow port reuse and SO_REUSEADDR
builder.WebHost.ConfigureKestrel((context, options) =>
{
options.ListenAnyIP(5054, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
});
});

// Configure data protection for Docker
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("/var/dpkeys"))
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ Link to the full guide - upcoming

### Development

During development, the container uses `dotnet watch` to enable automatic reloads when files change. The development server is started with:
During development, the container uses `nodemon` to enable automatic reloads when files and new dependencies change. The development server is started with:

```sh
dotnet watch --no-launch-profile --hot-reload --non-interactive
dotnet run \
--no-launch-profile \
--non-interactive
```

This will:
- Use `dotnet watch` to monitor for changes to .cs, .razor, and .css files and restart the server when changes are detected.
- Run the Blazor Server application with hot reload enabled.
- Start the app on port 5054.
- Enable automatic browser refresh when Razor components or CSS files change.
- Avoid using any launch profile so environment variables are controlled by the container.

### Installing Packages

Expand Down Expand Up @@ -64,7 +66,7 @@ dotnet add package Microsoft.EntityFrameworkCore.SqlServer

The `libman.json` file manages client-side libraries, while the `.csproj` file tracks NuGet dependencies. Both are automatically restored during development and build.

> **Important:** After installing packages, open the Deployment page and restart the running Blazor container so it loads the new dependencies.
`dotnet add package` updates the `.csproj` and restores the package to the local NuGet cache. `nodemon` notices the new package reference in `component-blazor.csproj` and restarts the runtime so the change is picked up immediately.

### Production

Expand Down
13 changes: 13 additions & 0 deletions nodemon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"watch": [
"."
],
"ignore": [
"**/bin/**",
"**/obj/**"
],
"ext": "cs,csproj,razor,cshtml,json,html,js,css",
"exec": "sh run-dotnet-watch.sh",
"signal": "SIGTERM",
"delay": "1000"
}
45 changes: 45 additions & 0 deletions run-dotnet-watch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -eu

PORT=5054

# Try graceful shutdown first
echo "=== Checking if port ${PORT} is in use ==="
if ss -ltnp 2>/dev/null | grep -q ":${PORT} "; then
echo "Port ${PORT} is in use, attempting graceful shutdown..."
pkill -TERM -f "dotnet watch" 2>/dev/null || true
pkill -TERM -f "dotnet run" 2>/dev/null || true
pkill -TERM -f "dotnet" 2>/dev/null || true
dotnet clean 2>/dev/null || true
sleep 2
fi

# Wait for port to be released (bounded wait)
echo "=== Waiting for port ${PORT} to be released ==="
for i in {1..50}; do
if ! ss -ltn 2>/dev/null | grep -q ":${PORT} "; then
echo "Port ${PORT} released."
break
fi
sleep 0.25
done

echo "=== Hard cleaning port ${PORT} if still busy ==="
# Hard kill only if still stuck
if ss -ltn 2>/dev/null | grep -q ":${PORT} "; then
echo "Port ${PORT} still busy, forcing release..."
fuser -k ${PORT}/tcp 2>/dev/null || true
sleep 2
fi

# Check for TIME_WAIT sockets (use ss -tan to see all TCP states, not just LISTEN)
echo "=== Checking for lingering TIME_WAIT sockets ==="
if ss -tan state time-wait 2>/dev/null | grep -q ":${PORT} "; then
echo "TIME_WAIT sockets found on port ${PORT}, waiting briefly..."
sleep 2
fi

echo "=== Starting running dotnet application"
exec dotnet run \
--no-launch-profile \
--non-interactive