Overview
Inkwell is a free, open-source, self-hosted blogging platform built on .NET 8. It ships as a single ASP.NET Core binary — no Node, no PHP, no external build pipeline. Install it on any server you control, point a domain at it, and write.
Core concepts
- Tenants — one Inkwell install can host many blogs. Each tenant has its own domain, Layout, Preset, authors, and database schema.
- Layouts — structural templates: Magazine, Journal, Notebook, Studio.
- Presets — surface styles (colour + type): Cream, Ink, Linen, Paper.
- Desk — the keyboard-first Markdown editor with live preview, autosave, footnotes, and pull quotes.
Requirements
| Runtime | Minimum version |
|---|---|
| .NET | 8.0 or later |
| Database | PostgreSQL 14+ or SQLite (dev only) |
| OS | Linux, Windows Server, macOS |
| Reverse proxy | nginx, Caddy, Apache, or IIS (recommended) |
Installation
1. Clone the repository
$ git clone https://github.com/marutisoftwaresolutions/blog $ cd blog
2. Restore packages
$ dotnet restore
3. Configure (see Configuration)
Edit appsettings.json or use environment variables for secrets.
4. Run database migrations
$ dotnet ef database update
5. Run
$ dotnet run --configuration Release
/desk/setup to complete configuration.
Quick start
Prefer a fast path? These three commands get you to a running instance with SQLite and a default tenant:
$ git clone https://github.com/marutisoftwaresolutions/blog && cd blog $ dotnet ef database update $ dotnet run
Open http://localhost:5000 and sign in with the credentials printed to the console on first boot.
Configuration
All settings live in appsettings.json. Override any value with an environment variable using the double-underscore convention: Inkwell__Tenants__0__Host.
Root structure
{
"Inkwell": {
"Tenants": [ ... ],
"Database": { ... },
"Email": { ... }
}
}
Multi-tenant setup
{
"Inkwell": {
"Tenants": [
{
"Host": "essays.example.com",
"Layout": "Magazine",
"Preset": "Cream"
},
{
"Host": "notes.example.com",
"Layout": "Notebook",
"Preset": "Linen"
}
]
}
}
Database
| Key | Default | Description |
|---|---|---|
Provider | Sqlite | Sqlite or Postgres |
ConnectionString | in-memory | Standard ADO.NET connection string |
Email (SMTP)
| Key | Description |
|---|---|
Email:SmtpHost | Hostname of your SMTP server |
Email:SmtpPort | Default 587 (STARTTLS) |
Email:EnableSsl | true recommended |
Email:SenderEmail | From address |
Email:RecipientEmail | Where inquiries are delivered |
Email:Username | SMTP auth username (leave blank if none) |
Email:Password | SMTP auth password — use a secret manager in production |
dotnet user-secrets locally and environment variables or a vault in production.
Layouts & Presets
Inkwell separates structure (Layout) from surface (Preset). Pick one of each per tenant. Swap them without a rebuild or a migration.
Layouts
| Name | Best for |
|---|---|
| Magazine | Multi-author publications with drop caps, bylines, departments |
| Journal | Solo diarists — one voice, one column, dated entries |
| Notebook | Short-form fragments, marginalia, public notes |
| Studio | Portfolio-shaped blogs — visual first, long-form behind |
Presets
| Name | Palette |
|---|---|
| Cream | Warm off-white background, dark ink text |
| Ink | Near-black background, cream text — dark by default |
| Linen | Warm linen, subtle texture, earthy tones |
| Paper | Pure white, high contrast, minimal |
Custom themes
A Layout is a set of Razor partials in /Views/Layouts/{Name}/. A Preset is a CSS variable override block in /wwwroot/css/presets/{name}.css. Both are hot-reloaded in development. See the GitHub repository for a full theming guide.
Deployment: Linux / systemd
$ dotnet publish -c Release -o /opt/inkwell $ sudo nano /etc/systemd/system/inkwell.service
[Unit] Description=Inkwell Blog [Service] WorkingDirectory=/opt/inkwell ExecStart=/opt/inkwell/Inkwell Restart=always RestartSec=10 User=www-data Environment=ASPNETCORE_ENVIRONMENT=Production [Install] WantedBy=multi-user.target
$ sudo systemctl enable --now inkwell
Deployment: Docker
A Dockerfile is included in the repository root. Build and run:
$ docker build -t inkwell . $ docker run -d -p 8080:8080 \ -e Inkwell__Database__ConnectionString="..." \ --name inkwell inkwell
A docker-compose.yml with Postgres and Caddy (for automatic TLS) is available in /deploy/compose/.
Deployment: IIS
- Install the .NET 8 Hosting Bundle on the server.
- Publish:
dotnet publish -c Release -o C:\inetpub\inkwell - Create a new IIS site pointing to
C:\inetpub\inkwell. - Set the application pool to No Managed Code.
- Ensure the app pool identity has read/write access to the publish directory.
Reverse proxy (nginx)
server {
listen 443 ssl;
server_name essays.example.com;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
Something missing? Open a discussion on GitHub or contact the team.