Portabase Logo
Portabase AgentDatabases

MsSQL

Specific configuration for MsSQL.

Add this database from the dashboard

Since Dashboard 1.29.0 and Agent 1.19.0, you can add this database directly from the dashboard (Add database on the agent page), without editing any file. See Adding a Database. The settings described below also apply to the dashboard form.

MsSQL is fully supported by the Portabase agent. By default it uses Microsoft's sqlpackage tool. For applications that need a native backup, the bak format runs BACKUP DATABASE instead.

DACPAC backups since Agent 1.22.0 and Dashboard 1.32.0

Backups are DACPAC files by default (sqlpackage /Action:Extract with all table data). The previous format, BACPAC (/Action:Export), is validated against Azure SQL Database rules and fails on on-premise servers that hold Windows principals such as NT AUTHORITY\SYSTEM (Error SQL71627). BACPAC remains available per database through the backup_format option.

Native .bak backups and safer restores since Agent 1.26.0 and Dashboard 1.36.0

The bak format adds native BACKUP DATABASE backups, for applications that reference other databases or are written to during backups. For every format, a failed restore now leaves the target database as it was. See Restore.

MsSQL has strict password complexity requirements. Your password must be at least 8 characters long and contain characters from three of the following four categories: Latin uppercase letters, Latin lowercase letters, digits (0 through 9), and non-alphanumeric characters (e.g., !, $, #, %). Failure to meet these requirements will cause the container to crash.

Configuration

When running portabase agent db add, select mssql as the database type.

Options

The following optional fields can be set under an options key in the database configuration. From the dashboard, they are the Backup format, Folder on SQL Server, Folder on the agent and Verify backup fields of the database form. The last three appear when you choose bak.

OptionTypeDefaultDescription
backup_formatstring"dacpac"Format of new backups: dacpac, bacpac or bak. See Backup formats below. If the value is absent or unrecognized, the agent uses dacpac and logs a warning. Agents older than 1.22.0 ignore this option and always produce BACPAC. Agents older than 1.26.0 do not know bak and produce a DACPAC.
bak_server_pathstringnoneRequired with bak. The shared folder as SQL Server sees it, for example D:\PortabaseBackups or \\nas\sql-backups.
bak_agent_pathstringnoneRequired with bak. The same folder as the agent sees it, for example /mnt/sql-backups.
bak_verifybooleanfalseWith bak, runs RESTORE VERIFYONLY ... WITH CHECKSUM after each backup. This reads the whole backup again on SQL Server.
databases.json (with options)
{
  "name": "Database - MsSQL",
  "database": "myappdb",
  "type": "mssql",
  "username": "sa",
  "password": "Password!Strong1",
  "port": 1433,
  "host": "db-mssql",
  "generated_id": "...",
  "options": {
    "backup_format": "bacpac"
  }
}

Backup formats

dacpac (default)bacpacbak
Methodsqlpackage Extract with all table datasqlpackage ExportBACKUP DATABASE on SQL Server
ContentSchema, data, database users, roles and permissionsSchema, data, database users and permissionsThe whole database, indexes included
Server loginsNot includedNot includedNot included
Windows principalsSupportedFails with SQL71627Supported
References to another databaseBackup succeeds. Restore fails if the referenced database is missingBackup fails with SQL71561 or SQL71562Backup and restore both succeed
Writes during the backupNot consistentNot consistentConsistent, a single point in time
SetupNoneNoneA folder shared with SQL Server
Azure SQL DatabaseYesYesNo

dacpac needs no setup and stays the default. Choose bak when the database references another database or master, when it is written to during backups, or when your application's vendor requires native backups. Use bacpac only when you need that file, for example to import it into Azure SQL Database from the Azure portal.

DACPAC and BACPAC are not transactionally consistent

sqlpackage reads tables one at a time. If the application writes during the backup, related rows in different tables can disagree in the backup, even when the application wrote them in one transaction, and nothing reports it. Back up during a quiet period, or use bak.

Native .bak backups

With backup_format set to bak, the agent asks SQL Server to write the backup into a shared folder:

BACKUP DATABASE [myappdb] TO DISK = '<bak_server_path>/<generated_id>.bak'
WITH COPY_ONLY, FORMAT, CHECKSUM, COMPRESSION

The agent then archives the file from that folder, sends it to your storages like any other backup, and removes it from the folder.

OptionWhy
COPY_ONLYDoes not disturb your own backups: your differential backups keep their base.
FORMATReplaces a file left by an interrupted run.
CHECKSUMStops the backup if SQL Server finds a corrupt page, so corruption shows up as a failed job.
COMPRESSIONUsed when your edition supports it. Otherwise, on Express for example, the backup runs again without it and the job log says so.

Shared folder

SQL Server writes the file on its own machine, so the agent must be able to reach that folder. You provide the folder and set two paths, because each side sees it differently.

  • The SQL Server service account must be able to write there.
  • The agent must be able to read, write and delete there. On Linux, SQL Server creates the file as 0640 mssql:root. The agent image runs as root and can read it.

If the two paths do not point to the same folder, the job fails and the error names both. At most one file per database can stay behind if the agent cannot delete it, and the next backup overwrites it.

For a SQL Server on Windows, share the folder and mount it into the agent:

docker-compose.yml
services:
  agent:
    image: portabase/agent:latest
    # ... agent configuration ...
    volumes:
      - sql-backups:/mnt/sql-backups

volumes:
  sql-backups:
    driver: local
    driver_opts:
      type: cifs
      device: //sqlhost/PortabaseBackups
      o: "username=portabase,password=${SHARE_PASSWORD},uid=0,gid=0"
databases.json (options)
{
  "options": {
    "backup_format": "bak",
    "bak_server_path": "D:\\PortabaseBackups",
    "bak_agent_path": "/mnt/sql-backups"
  }
}

With the CLI, -o bak_agent_path=/mnt/sql-backups mounts that host folder into the agent at the same path. Mount the share on the host first.

Applications that spread their data across several databases need them backed up close together. Put them in the same project and set the schedule on the project: all of them start at the same time, unless MAX_CONCURRENT_BACKUPS makes them wait. To restore them together, select them on the project page and use Restore latest.

Each backup is consistent on its own, but the set is not one point in time across databases.

Limits

  • Azure SQL Database and Managed Instance cannot back up to disk: the job fails with the server's error. Use dacpac or bacpac there.
  • A .bak file does not restore onto an older SQL Server version. The restore is refused before anything changes.
  • Server logins are not in the backup. Database users restored onto another server need matching logins.
  • DB_CHAINING and TRUSTWORTHY are OFF after a restore. If your application relies on them, set them again.
  • The transaction log is not truncated. In the FULL recovery model, log backups remain your responsibility.
  • Size. A .bak keeps indexes and pages, so it is usually larger than a DACPAC of the same database.

Restore

A restore replaces the target database. The method follows the file extension, not the backup_format option, so a database can restore any of its older backups.

FileHow the target is replaced
.dacpac, .bacpacsqlpackage builds the backup in a temporary side database named <target>_restore_<generated_id>_<run> (/Action:Publish or /Action:Import). The target stays online with its current data. Once that succeeds, the agent disconnects the sessions, drops the target and renames the side database. If the target does not exist, sqlpackage restores straight into it.
.bakSQL Server checks that it can read the file, then runs RESTORE DATABASE ... WITH REPLACE from the shared folder. The database options above must be set on the target. Each file keeps its path when the target already has it, and otherwise goes to the instance's default data or log folder.

The restore is refused before anything changes when:

  • the file is not a .dacpac, .bacpac or .bak;
  • the target is a system database (master, tempdb, model, msdb);
  • for .dacpac and .bacpac, the configured login cannot create databases or replace the target. It needs sysadmin or dbcreator, or CREATE ANY DATABASE together with db_owner of the target, or, on Azure SQL Database, the server admin or the dbmanager role;
  • for .bak, the shared folder options are missing, or SQL Server cannot read the file.

If sqlpackage fails, the side database is removed and the target is left untouched. A side database left by an interrupted run is removed by the next restore of that database.

What to expect with .dacpac and .bacpac

  • Free space. The server holds the target and the restored copy at the same time. Without enough room the restore fails and the target stays as it was. To restore anyway, drop the target by hand first, and the restore then goes straight in.
  • File names. After a restore over an existing database, its files and logical names keep the side database's name, for example myappdb_restore_..._Primary.mdf. The database itself has the right name.
  • Server logins referenced by database users must exist on the target server. Logins are not part of the backup, so restoring onto another server (for example a backup holding Windows principals onto SQL Server on Linux) fails if they are missing. The target is left untouched.
  • Azure SQL Database. The restored database gets the default service objective and is no longer part of its elastic pool, and the side database is billed while it exists.
  • Rename failure. If the final rename fails after the target was dropped, the job error names the side database, which holds the restored data. Rename it by hand. The next restore removes it.
  • Agents older than 1.26.0 drop the target first, with no way back: if sqlpackage fails, the target database is gone. Upgrade before restoring anything you cannot lose.

Importing a Backup

Since Dashboard 1.32.0, the Import dialog accepts a raw .dacpac or .bacpac file, or a .tar.gz archive containing one. Since Dashboard 1.36.0, it also accepts .bak. The file keeps its extension, which tells the agent how to restore it. Any other file name is rejected.

Uploads are limited to 2 GB, which a .bak can exceed. Backups taken by the agent have no such limit. Restoring a .bak needs the shared folder options on the target database, whatever its backup_format.

Docker Compose Example

Here is how to configure a MsSQL service alongside the agent.

docker-compose.yml
services:
  db-mssql:
    container_name: db-mssql
    image: mcr.microsoft.com/azure-sql-edge:latest
    ports:
      - "1433:1433"
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "Password!Strong1"
    volumes:
      - mssql-data:/var/opt/mssql
    networks:
      - portabase
    healthcheck:
      test: ["CMD-SHELL", "cat /proc/net/tcp6 | grep -q '059901' || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 20

  agent:
    image: portabase/agent:latest
    # ... agent configuration ...
    depends_on:
      - db-mssql
    networks:
      - portabase

networks:
  portabase:
    external: true

volumes:
  mssql-data:

If you use localhost as the host (because the agent is on the host machine and not in Docker, or via host-gateway), ensure your database is listening on all interfaces (0.0.0.0) or is accessible from the agent.

Last updated on

On this page