Deployment — Backup & Rollback¶
What holds the NKO ERP's state, how to back it up so it can be restored consistently, and how to roll a release back — including the cases where a rollback cannot be done without restoring data.
Nothing in the repository automates any of this: there are no backup jobs, scripts or scheduled tasks. Everything below is procedure, derived from where the application actually keeps its data.
What holds state¶
| Store | What is in it | How to back it up |
|---|---|---|
NKO_DB |
Everything except the warehouse: employees and logins, permissions, HR, payroll, procurement, finance, recruitment, training, notifications | SQL Server backup — together with NKO_DB_Warehouse |
NKO_DB_Warehouse |
Stock, assets, maintenance, destruction, waybills | SQL Server backup — together with NKO_DB |
| Hangfire database | Queued and failed background jobs — the warehouse Excel exports | Optional. Hangfire recreates its schema (Prepare); losing it loses only in-flight exports. |
AWS S3 — one folder per feature (Storage) |
Every uploaded document: attachments, contracts, signatures, RFQ quotes, GRN and PO files | Enable bucket versioning. The application calls Delete, so a deleted object is gone unless versioning keeps it. |
C:\upload\nko\files\user |
Employee profile photos — saved to local disk, not to S3. The database stores only the file name. | File backup. Lose this folder and every photo is gone, with the database still pointing at the missing files. |
C:\upload\nko\templates\ |
e-sign., contract. |
File backup — they cannot be regenerated by the application |
Other C:\upload\… folders |
Generated Excel exports | Not needed — regenerated on demand |
wwwroot\Rotativa\ |
wkhtmltopdf binaries |
Keep a copy — they are not in the repository (R5-3) |
| Server configuration | appsettings. or environment variables — connection strings, AWS keys |
Keep a secure copy; it is not in the repository |
Back up the two databases together¶
The core and warehouse databases have no foreign keys between them, but they refer to each other anyway:
- warehouse rows store core ids —
UserId,CreatedBy,ProjectId, GRN numbers — as plain integers; BGRN.TransfareToWarehousewrites both databases in one operation — stock and assets intoNKO_DB_Warehouse, then theIsTransfareToWarehouseflag intoNKO_DB.
That transfer is not atomic: it calls ware_db.SaveChangesAsync() and then
db.SaveChangesAsync() as two separate commits, and there is no transaction anywhere in the solution
(R7-18). If the two backups are taken at different moments, a restore can produce GRNs marked
as transferred with no stock behind them, or stock that points at a GRN still awaiting transfer —
which the next transfer would then duplicate.
So:
- back up both databases in the same window, ideally with the site offline
(
app_offline.htm) so nothing is written between the two; - or run both in the full recovery model with log backups, and restore both to the same point in time.
Before every release¶
- [ ] Full backup of
NKO_DBandNKO_DB_Warehouse, taken together. - [ ] Copy of
C:\upload\nko\files\userImages\andC:\upload\nko\templates\. -
[ ] Record the last applied migration of each context:
-
[ ] Keep the previous publish package, so the old build can be redeployed without rebuilding.
- [ ] Read the new migration scripts for
DROP TABLE/DROP COLUMN— see below.
Rolling back¶
flowchart TD
A{"Did the release apply migrations?"}
A -- no --> B["Redeploy the previous package"]
A -- yes --> C{"Does any of them drop a table or column?"}
C -- no --> D["Revert the schema, then redeploy the previous package"]
C -- yes --> E["Restore BOTH databases from the pre-release backup,<br/>then redeploy the previous package"]
Code only¶
Redeploy the previous package with Web Deploy, keeping extra files on the server (see Build & Publish). The schema is unchanged, so nothing else is needed.
With non-destructive migrations¶
Revert each context to the migration recorded before the release. The generated script can be reviewed before it runs:
dotnet ef migrations script <NewMigration> <PreviousMigration> --context NKO_DBEntities1 `
--project ../../NKO.Lib/NKO.DataAccess --startup-project . -o rollback-core.sql
This runs each migration's Down() method in reverse order. Then redeploy the previous package.
With destructive migrations — restore instead¶
A migration that drops a table or column has a Down() that re-creates the schema — but the
data is gone. Running it "succeeds" and leaves empty tables and null columns. For these the only
real rollback is to restore both databases from the pre-release backup and redeploy the
previous package. Anything entered between the release and the restore is lost, so decide quickly.
Which migrations destroy data¶
Nine of the 23 existing migrations drop tables or columns — all in the core context; none of the five warehouse migrations do:
| Migration | Tables / columns dropped |
|---|---|
20250420061112_remove |
10 |
20250420064809_remove |
1 |
20250507120311_remove |
2 |
20250619063304_job |
1 |
20250619064002_job |
2 |
20250718162830_remove |
12 |
20250718174446_remove |
5 |
20260729094813_Project |
3 |
20260730200327_fin |
4 |
These are already applied, so they matter when rolling a database back past them — for example to run an older build against a copy of production. Future migrations should be checked the same way before every release.
After a restore¶
- [ ] Restore both application databases from the same backup set.
- [ ] Clear the Hangfire database, or expect its queued jobs to reference rows that no longer exist.
- [ ] Redeploy the build that matches the restored schema — the last applied
MigrationIdmust match the build's migrations. - [ ] Run the smoke test.
- [ ] Check that open requests still have a valid
NextUserId, or run Refresh Next users with All at/Admin/TriggerNextUserrather than waiting for 03:00 (and see R8-11).
Related¶
- Build & Publish · Server Requirements · Configuration
- Database — the two contexts and what they hold.
- Recommendations — R4-1 … R4-5, R7-18.