Logo

SQL Server Express: installation and auto backups

SQL Server Express: installation and auto backups

Install free SQL Server Express on your Windows VPS, secure it and automate backups. The Express edition is limited but enough for most business apps (10 GB per database, 1 GB usable RAM, 4 cores).

Introduction

SQL Server Express is the free edition of SQL Server by Microsoft. Limitations:

  • 10 GB max per database
  • 1410 MB RAM used for buffer pool
  • 4 max CPU cores
  • No SQL Server Agent (auto jobs)

But for many Windows apps (.NET, business ERPs, small ETLs), it's largely enough. And it's free in production without license.

Prerequisites

  • Windows Server 2019 / 2022 / 2025
  • At least 4 GB RAM
  • 5 GB for install + space for your data
  • Admin access

Step 1: Download SQL Server Express

Download the latest version (SQL Server 2022 Express):

# Direct download
$url = "https://go.microsoft.com/fwlink/p/?linkid=2216019"
Invoke-WebRequest -Uri $url -OutFile "C:\Temp\SQL2022-SSEI-Expr.exe"

Or via site: https://www.microsoft.com/en-us/sql-server/sql-server-downloads

Step 2: Installation

cd C:\Temp
.\SQL2022-SSEI-Expr.exe

In the wizard:

  1. Basic: simple install (recommended to start)
  2. Custom: advanced install with component choice
  3. Download Media: downloads ISO for offline install or other servers

Click Basic → Accept → Install.

Install takes ~10 minutes.

Step 3: Note the connection string

At the end, installer displays:

Instance name: SQLEXPRESS
Connection string: Server=localhost\SQLEXPRESS;Database=master;Trusted_Connection=True;

Save it. You'll need it for your apps.

Step 4: Install SQL Server Management Studio (SSMS)

To manage SQL Server visually:

$ssmsUrl = "https://aka.ms/ssmsfullsetup"
Invoke-WebRequest -Uri $ssmsUrl -OutFile "C:\Temp\SSMS-Setup-ENU.exe"
Start-Process "C:\Temp\SSMS-Setup-ENU.exe" -ArgumentList "/install /quiet /norestart" -Wait

Launch SQL Server Management Studio from Start menu.

Step 5: First connection

In SSMS, at the connection screen:

  • Server name: localhost\SQLEXPRESS
  • Authentication: Windows Authentication
  • Connect

You access the tree: Databases, Security, Server Objects, etc.

Step 6: Enable SQL authentication (mixed mode)

By default SQL Express uses Windows Authentication only. To allow SQL logins:

  1. Right-click the server (tree root) → Properties
  2. SecuritySQL Server and Windows Authentication mode
  3. OK

Enable the sa account:

  1. Security → Logins → sa → Properties
  2. General: set a strong password
  3. Status: Login Enabled

Restart the service:

Restart-Service -Name "MSSQL`$SQLEXPRESS"

Step 7: Create a database

Via SSMS (GUI)

Right-click Databases → New Database → name: MyApp → OK.

Via SQL

CREATE DATABASE MyApp
ON PRIMARY (
    NAME = 'MyApp_data',
    FILENAME = 'D:\SQLData\MyApp.mdf',
    SIZE = 100MB,
    MAXSIZE = 10GB,
    FILEGROWTH = 100MB
)
LOG ON (
    NAME = 'MyApp_log',
    FILENAME = 'D:\SQLLogs\MyApp.ldf',
    SIZE = 50MB,
    MAXSIZE = 1GB,
    FILEGROWTH = 50MB
);

⚠️ Put data and logs on separate disks if possible (I/O perf).

Step 8: Create a dedicated app user

Avoid using sa from your app.

-- Create login
USE master;
CREATE LOGIN myapp_user WITH PASSWORD = 'AStrongPassword_2024!';

-- Grant DB access
USE MyApp;
CREATE USER myapp_user FOR LOGIN myapp_user;

-- Permissions
ALTER ROLE db_datareader ADD MEMBER myapp_user;
ALTER ROLE db_datawriter ADD MEMBER myapp_user;
-- For DDL (CREATE TABLE, etc.):
-- ALTER ROLE db_ddladmin ADD MEMBER myapp_user;

.NET connection string:

Server=localhost\SQLEXPRESS;Database=MyApp;User Id=myapp_user;Password=AStrongPassword_2024!;TrustServerCertificate=True;

Step 9: Enable TCP/IP (for remote connections)

By default SQL Express listens only on named pipes (local).

# Open SQL Server Configuration Manager
SQLServerManager16.msc  # SQL 2022, adapt per version
  1. SQL Server Network Configuration → Protocols for SQLEXPRESS
  2. TCP/IP → Enabled: Yes
  3. Right-click TCP/IP → Properties → IP Addresses
  4. At bottom, IPAll → TCP Port: 1433
  5. OK
  6. Restart service:
Restart-Service -Name "MSSQL`$SQLEXPRESS"

Open firewall:

New-NetFirewallRule -DisplayName "SQL Server" -Direction Inbound -Protocol TCP -LocalPort 1433 -Action Allow

⚠️ If SQL is exposed, restrict to allowed IPs:

New-NetFirewallRule -DisplayName "SQL Server" -Direction Inbound -Protocol TCP -LocalPort 1433 -RemoteAddress "10.0.0.0/24" -Action Allow

Step 10: Automatic backup

SQL Express has no SQL Server Agent. Use Windows Task Scheduler.

PowerShell backup script

# C:\Scripts\backup-sql.ps1
$Server = "localhost\SQLEXPRESS"
$BackupDir = "D:\Backups\SQL"
$Date = Get-Date -Format "yyyy-MM-dd_HHmm"
$Retention = 14  # days

if (-not (Test-Path $BackupDir)) {
    New-Item -ItemType Directory -Path $BackupDir | Out-Null
}

# Backup all user databases (skip system)
$Databases = Invoke-Sqlcmd -ServerInstance $Server -Query @"
SELECT name FROM sys.databases 
WHERE database_id > 4 
AND state_desc = 'ONLINE'
"@

foreach ($db in $Databases) {
    $DbName = $db.name
    $BackupFile = "$BackupDir\$DbName`_$Date.bak"
    
    Write-Host "Backup of $DbName..."
    
    Invoke-Sqlcmd -ServerInstance $Server -Query @"
BACKUP DATABASE [$DbName] 
TO DISK = '$BackupFile' 
WITH FORMAT, COMPRESSION, INIT, STATS = 10
"@ -QueryTimeout 3600
}

# Rotation
Get-ChildItem -Path $BackupDir -Filter "*.bak" | 
    Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-$Retention) } | 
    Remove-Item -Force

Write-Host "Backup completed. Files in $BackupDir"

⚠️ Install the Sqlcmd module if needed:

Install-Module -Name SqlServer -Scope AllUsers -Force

Schedule as Windows task

$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\backup-sql.ps1"
$Trigger = New-ScheduledTaskTrigger -Daily -At 2am
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$Principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest

Register-ScheduledTask -TaskName "SQL Backup Daily" `
    -Action $Action -Trigger $Trigger -Settings $Settings -Principal $Principal

Check:

Get-ScheduledTask -TaskName "SQL Backup Daily" | Get-ScheduledTaskInfo

Step 11: Test restore

A backup not tested doesn't exist.

-- Restore to new DB for test
RESTORE DATABASE MyApp_test
FROM DISK = 'D:\Backups\SQL\MyApp_2026-05-16_0200.bak'
WITH MOVE 'MyApp_data' TO 'D:\SQLData\MyApp_test.mdf',
     MOVE 'MyApp_log'  TO 'D:\SQLLogs\MyApp_test.ldf',
     REPLACE, STATS = 10;

Step 12: Sync backups off-site

Backups on the same VPS don't protect against server loss. Sync to external storage.

To Backblaze B2 with rclone

# Install rclone
choco install rclone -y
# or download: https://rclone.org/downloads/

# Configure
rclone config
# n → b2 → name: b2-sql → enter Backblaze credentials

# Daily sync (integrate into backup script)
rclone sync "D:\Backups\SQL" "b2-sql:verycloud-sql-backups/" --transfers 4

To OneDrive / Azure Blob

Same, rclone supports 70+ providers.

Step 13: Regular maintenance

Update statistics

USE MyApp;
EXEC sp_updatestats;

Rebuild fragmented indexes

USE MyApp;
DECLARE @sql NVARCHAR(MAX) = '';
SELECT @sql = @sql + 'ALTER INDEX ' + QUOTENAME(i.name) + 
    ' ON ' + QUOTENAME(s.name) + '.' + QUOTENAME(o.name) + 
    ' REBUILD;' + CHAR(13)
FROM sys.indexes i
JOIN sys.objects o ON i.object_id = o.object_id
JOIN sys.schemas s ON o.schema_id = s.schema_id
JOIN sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ps
    ON i.object_id = ps.object_id AND i.index_id = ps.index_id
WHERE ps.avg_fragmentation_in_percent > 30
  AND o.type = 'U';

EXEC sp_executesql @sql;

Schedule weekly.

Shrink (avoid except critical)

DBCC SHRINKDATABASE (MyApp, 10);

⚠️ Causes fragmentation. Only use after a large data purge.

Troubleshooting

"Cannot connect to localhost\SQLEXPRESS"

Get-Service -Name "MSSQL`$SQLEXPRESS"
Start-Service -Name "MSSQL`$SQLEXPRESS"

Also check SQL Server Browser:

Set-Service -Name "SQLBrowser" -StartupType Automatic
Start-Service -Name "SQLBrowser"

"Database is at capacity (10 GB limit)"

You reached the Express limit. Solutions:

  • Purge old data
  • Migrate to SQL Server Standard (paid)
  • Migrate to PostgreSQL (free, no limit)

Backup fails with "Access denied"

SQL Server account lacks rights on backup folder:

icacls "D:\Backups\SQL" /grant "NT SERVICE\MSSQL`$SQLEXPRESS:(OI)(CI)F"

Degraded performance

Check:

  • Buffer pool saturated? SELECT * FROM sys.dm_os_performance_counters WHERE counter_name = 'Buffer cache hit ratio';
  • Missing indexes? Database Engine Tuning Advisor in SSMS

Useful commands

-- Version
SELECT @@VERSION;

-- Databases and sizes
SELECT name, 
       SUM(size * 8 / 1024) AS Size_MB
FROM sys.master_files
WHERE database_id > 4
GROUP BY name;

-- Active sessions
SELECT session_id, login_name, host_name, program_name, status
FROM sys.dm_exec_sessions
WHERE is_user_process = 1;

-- Top slow queries
SELECT TOP 10
    qs.execution_count,
    qs.total_elapsed_time / 1000 / qs.execution_count AS avg_ms,
    SUBSTRING(qt.text, qs.statement_start_offset/2+1,
        (CASE qs.statement_end_offset
            WHEN -1 THEN DATALENGTH(qt.text)
            ELSE qs.statement_end_offset
        END - qs.statement_start_offset)/2 + 1) AS query
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
ORDER BY avg_ms DESC;

-- Kill a session
KILL <session_id>;
# Service status
Get-Service -Name "MSSQL`$SQLEXPRESS"

# Restart
Restart-Service -Name "MSSQL`$SQLEXPRESS"

# SQL Server logs
Get-Content "C:\Program Files\Microsoft SQL Server\MSSQL16.SQLEXPRESS\MSSQL\Log\ERRORLOG"

Conclusion

SQL Server Express is perfect for:

  • Small business apps (.NET)
  • Databases < 10 GB
  • Dev / test
  • Legacy apps requiring SQL Server

SQL Express vs Standard limits:

FeatureExpressStandard
DB size10 GB524 PB
Buffer pool RAM1.4 GB128 GB
CPU4 cores24 cores
SQL Agent
Always On✅ (partial)
Replication
PriceFree~$3500/core

Going further:

  • Migrate to PostgreSQL if you want free + unlimited
  • Move to SQL Server Standard if you need SQL Agent and > 10 GB
  • Consider Azure SQL Database for managed cloud-native

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members