File Encryption

Turns FileMaker’s encryption at rest (EAR) on or off for a .fmp12, or changes the passcode of a file that already has one, using FMDeveloperTool. Unlike most of the portal’s workflows this one does not run through AWX — the portal runs the tool over SSH itself.

Primary use: Bring an unencrypted database under encryption at rest, or rotate the passcode of one that is already encrypted.

Save the passcode somewhere secure before you run an encrypt. Use a password manager. The portal itself does not keep it, and a file with encryption at rest cannot be opened, migrated, refreshed, recovered or restored from backup without it.

If you lose it, contact DevOps. When a run hosts an encrypted file, the portal files the new passcode with the DevOps team automatically, so there is a backup to fall back on — see What the portal saves, and when. That does not apply to a run that only stages its result, or to one that fails: in those cases your copy is the only one.

FMDeveloperTool cannot read an open file. If you work on a live database, FileMaker Server closes it first and clients are disconnected from it until it is reopened. How long that lasts depends on which result you choose — see Downtime.

Mac servers are supported here, unlike File Compact, which drives an Ubuntu-only ansible role. This workflow runs the tool over the portal’s own SSH, and every step that differs between the two platforms already accounts for both. What a mac host does need is FMDeveloperTool reachable for the fmserver account: the Linux hosts get it on PATH from the ansible roles, while on a mac the portal also looks in /Library/FileMaker Server/Database Server/bin/ and /usr/local/bin/. If it is somewhere else, symlink it into one of those — the run refuses up front with a message saying so, before anything is closed or written.

All pages in this flow show an Experimental warning banner.

The wizard has six steps:

  1. Select a client
  2. Select the host
  3. Select the file
  4. Choose the change
  5. Choose what happens to the result
  6. Review and run

1. Select a client

Choose the client group whose server holds the file. Every client with at least one server you can access is listed — no deploy chain is required, because the run happens on a single host.


2. Select the host

Choose the FileMaker Server the file lives on. Only servers you have access to within the selected client are shown, mac hosts included. Production servers display a red badge.


3. Select the file

The source is addressed by full path, so the file does not have to be hosted. Two tabs:

  • Backup file — the server’s backup directory, in the same browser Host from Backup — On-Server uses. Nothing that is hosted is touched and nothing goes offline, which makes this the way to prove a passcode works before it becomes the passcode of a live database. Use a full backup; a progressive backup gives no reliable sign that it has finished being written.
  • Live hosted file — the databases FileMaker Server is serving. Only a live source can be swapped in step 5, and only a live source is closed for the run.

Either source needs the database’s container objects with it. Every operation writes a new file, so each externally stored container object is copied into it — and a backup carries them only if the server was configured to include its container data folder (Back Up Container Data Folder, in the admin console), which many servers deliberately do not. Without them the run works through the whole database and then fails with Error: (853): One or more containers failed to transfer. Leave open-storage container data outside the secure containers does not avoid this — objects in secure storage are carried across either way.

A backup that is missing them is refused, at step 4 and again on the review step, with the Run button disabled. The portal works this out by looking at the database’s live counterpart on the same server: if that has container objects and the backup does not, the run cannot succeed. Encrypt the live file instead — and choose Host it beside the original, under a new name if you want the source database left alone. The live objects are deliberately not borrowed for a backup: objects as they are now do not belong to records as they were then.

A live file whose objects are in a separate container data folder is handled. FMDeveloperTool looks for container objects beside the file it opens and cannot read FileMaker Server’s configured Container Data Folder — the server supplies that location itself, and the tool runs without the server. So the run adds a second directory entry for the database in the folder that holds its RC_Data_FMS, points the tool at that, and removes the entry afterwards. It is a hard link: nothing is copied, the database is not modified, and removing the link removes one name for a file that keeps its own. The review step says so before the run. A symbolic link was tried first and the tool did not resolve objects through it.

Two things stop it, both refusals rather than attempts: a file of the database’s name already in that folder, and a container folder on a different volume (a hard link needs one filesystem).

A database with no container data is unaffected: nothing to copy cannot fail to copy, and a backup stays the way to try a passcode without taking anything offline.

The check finds container data that is absent, not container data that is stale or partial. A backup whose objects were captured months ago looks fine and can still fail — the run then names the counts and the affected fields, which takes about five minutes on a 20 GB database.

The portal reads the file’s encryption header on the way to step 4, so the next step opens knowing whether the file is already encrypted, and offers only the changes that can apply to it.


4. Choose the change

Change Tool command Needs
Encrypt — turn encryption at rest on --enableEncryption A new passcode and a shared ID
Decrypt — turn encryption at rest off --removeEncryption The passcode the file has now
Change the encryption passcode (no single command — see below) Both

An option that cannot apply to the file is disabled with the reason shown: you cannot encrypt a file that is already encrypted (change its passcode instead), and you cannot decrypt or re-key one that is not encrypted.

Changing the passcode

FMDeveloperTool has no re-key command, so the portal runs two: --removeEncryption into the staging folder, then --enableEncryption out of it under the new passcode. Two consequences worth knowing:

  • A decrypted copy of the database exists on the server’s disk for the length of the second command. It lives only inside the run’s staging folder, and it is removed with shred (falling back to rm) as soon as the second command finishes — including when the run fails or is cancelled.
  • It needs two files’ worth of temporary disk space rather than one. The disk projection in step 6 accounts for this.

The source file itself is never modified by any of the three operations. A failure at any point leaves the original exactly as it was.

The current passcode

Required for a decrypt and for a re-key. FMDeveloperTool cannot read an encrypted file without it.

There is no fallback: the portal does not look this up anywhere, so a file whose passcode you do not have is a file that cannot be changed here. This is deliberate — a workflow that silently reaches into a shared vault for a key is one where nobody can tell, afterwards, which passcode a run actually used.

The value is used for the one run and then discarded.

The new passcode

Needed by an encrypt and by the second half of a re-key. Entered twice to catch a typo, because a typo here produces a file that opens with a passcode nobody knows.

Field Tool option Meaning
New passcode -passcode The EAR password the result will have.
Shared ID -sharedID Files that share this ID and a passcode open each other without prompting. Defaults to the host name, on the assumption that the files sharing a passcode are the files on one server. Change it to match the rest of a solution whose files already use something else.
Passcode hint -passcode_hint Optional. Shown by FileMaker when the passcode is asked for. Not a place to put the passcode.
Leave open-storage container data outside the secure containers -keep_open_storage Off by default, matching the tool. With it off, externally stored container data is moved into secure containers so it is encrypted too — which is usually the point, and why encrypting a file with a lot of container data can take far longer than its .fmp12 size suggests. Tick it to leave that data in the clear. Either way the objects are copied into the new file’s own container folder — this does not skip them, it only chooses which kind of storage they land in.

File account

FMDeveloperTool signs in to the file with a full-access file account — a different thing from the FileMaker Server admin console login, and not your portal login.

Required, for the same reason the current passcode is: nothing is looked up for you. The account name defaults to Admin and the password may be left blank, which is what a file whose accounts have never been changed still uses. Neither value is stored.

How what you type is handled

Everything secret on this step — both passcodes, the hint, the file account password — is posted once, stored in the server-side session under a one-shot token, and the token is what travels through the remaining wizard steps. So:

  • No passcode is ever written into a page, a hidden form field, or the progress page’s JavaScript.
  • No passcode is written to the FileEncryptOperation row, the history, the progress snapshots, or the log.
  • The stash expires after 30 minutes. Take longer than that over the review step and step 6 will send you back to type it again.
  • The token is spent when the run launches, so a page reload cannot start a second run.

One exposure remains and cannot be closed here: FMDeveloperTool accepts the passcode only as a command-line option, so while the tool is running the passcode is visible in ps to anyone with a shell on that host. The portal keeps it out of the ssh and shell command lines (the script is fed on stdin) and scrubs it from anything it displays or logs, but the tool’s own process arguments are the tool’s to decide.


5. Choose what happens to the result

The tool always writes a new file into a staging folder under the server’s backup directory. This step decides what becomes of it — the same three outcomes the File Compact flow offers.

Outcome What happens
Leave it in the staging folder Nothing is hosted and nothing is replaced. The result stays on the server for you to check, download, or host yourself. The way to confirm a passcode works before it becomes a live database’s passcode.
Host it beside the original, under a new name The result is placed in the databases folder under a name you choose and opened. Create-only: a clashing name is refused, so this can never clobber a live file — see What counts as a clashing name.
Replace the source file with it The result takes the source’s place and name. The only outcome that changes what the server actually serves.

Externally stored container data lives in a folder keyed on the database name, so container fields in a result hosted under a new name will not resolve. Only the swap keeps the name, and with it the container data and any client file references.

What counts as a clashing name

FileMaker resolves a hosted database by name, so the check is broader than “is there a file called that in the folder I am writing to”:

  • Every folder the server hosts from, not just the default one. A file of that name in a secondary database folder can never be opened alongside one in the default folder, so it is a clash even though the destination folder is empty of it.
  • Regardless of capitalisation. On the Linux hosts Live.fmp12 and live.fmp12 are two files on disk — but the server cannot tell them apart as hosted names, and creating the second is how you get a database that will not open for no visible reason. On a mac host’s case-insensitive volume they are the same file, so the check is right there too.
  • Including files that are closed. The check reads the filesystem rather than the list of open databases, so a file FMS is not currently serving still holds its name.

The same check runs twice: on the review step, so a name that will be refused is refused while the field is still in front of you, and again in the worker immediately before anything is written, because minutes have passed and another job may have taken the name. The second check happens before the source database is closed, so a refusal costs no downtime. The refusal names the full path of the file that clashed.


6. Review and run

Shows the host, the source and its size, the change, the shared ID, what happens to container data, and the outcome — plus the disk projection and any warnings that apply.

The Run button is disabled when the change cannot apply to the file, when a new-name target is already in use, or when the disk policy refuses the run.

Disk space

The projection charges the source’s size for the result, plus the size of its externally stored container objects — measured with du on the container folder, and shown beside “Container data” on the review step. Those bytes matter because every operation writes a new file, and a new file needs its own copy of the objects: an encrypt with the default settings pulls them into the file itself, which is why a result can be several times the size of the source .fmp12. A database with a 2 GB file and a 14 GB container tree needs 16 GB, not 2 GB, and the disk policy gates on this number. On top of that:

  • a re-key charges a second copy of both, because the intermediate and the final file both exist at the peak. The intermediate is released before the run ends.
  • a swap charges the pre-replace backup as well, and hands the source’s bytes back at the end, because the result takes its place.

If the container folder cannot be measured, the run is not refused — the estimate simply excludes it, and the disk card’s “could not project this” acknowledgement applies.

Back up before replacing

Shown for a swap, on by default. Copies the current file to a timestamped folder under pre-encryption-backups/ on the server first — this is what makes a swap reversible.

The backup is encrypted exactly as the current file is. For an encrypt run that means the backup is unencrypted, and it stays that way on disk. If the point of the run is that this data must not sit in the clear, move or remove that backup once you are satisfied with the result.

Downtime

Source and outcome What clients see
Backup source, any outcome Nothing. No hosted file is closed.
Live source, staged or new name The source is closed while the tool runs and reopened as soon as the result is written.
Live source, swap The source is closed before the tool starts and is not served again until the result has replaced it and been opened — the whole job, not a moment. Do this in a maintenance window.

The passcode is saved on the server, so the file reopens by itself

FileMaker Server asks for an encrypted database’s passcode every time it starts, so a file opened with a one-shot key stays closed after the next restart until somebody supplies it again. The portal therefore opens an encrypted result with

fmsadmin open -u <admin> -p <password> --yes --savekey --key <passcode> <file>

which puts the key in the server’s keystore — the same invocation, flags included, that bz.server_czar’s fms_common open-file task uses after a compact. Nothing further is needed on the host.

Two consequences worth knowing:

  • This is the one place the flow prefers fmsadmin over the Admin API, whose open takes a key for that single open and does not persist it. A decrypt still uses the Admin API, since there is no key to save.
  • If the --savekey step fails, the run still opens the database through the Admin API rather than leaving it closed, and the result page says the key was not saved, gives fmsadmin’s own reason for the failure, and quotes the command to run by hand. A served database that needs its key after a reboot beats one that is not served at all.

Progress and cancelling

The run happens on the server in a background worker, so closing the page does not stop it. The progress page follows it by polling; teammates can watch the same run from the History page.

FMDeveloperTool reports no progress of its own, so the bar is indeterminate and the phase label is the granularity available: closing the file, running the tool (step 1 or 2 of 2 for a re-key), verifying the result, hosting it, opening it.

Cancel stops the run before anything is hosted, clears the staging folder, and reopens the source. Once the swap’s mv has happened there is nothing left to cancel; the run is over in the same moment.

Verifying the result

Before anything is hosted, the portal re-reads the result’s encryption header and confirms it actually changed in the direction the run asked for. A tool that exits zero having produced a file in the wrong state — an encrypt that left the file unencrypted, say — fails the run here rather than quietly hosting it behind a green tick.


What the portal saves, and when

The portal reads nothing out of 1Password for this workflow. It writes one thing.

When a run encrypts a file (an encrypt, or a re-key) and hosts the result, the new passcode is written to 1Password. This is the DevOps team’s recovery copy, and it is what makes “ask DevOps” the answer to a lost passcode.

It is a fallback, not a filing system. Operators should still keep their own copy; asking DevOps to read a key out of the vault should be the exception.

Nothing reads this vault yet. The ansible roles look up fms-ear-key_* in the deployment vault (op item get --vault "", defaulting to Bz Deployment Secrets), and the portal writes to OP_EAR_VAULT. So filing the passcode does not currently make scheduled jobs work: after you encrypt a hosted database, any data migration, refresh or restore of it will be unable to open the file until DevOps add the same entry to the deployment vault by hand.

The portal’s own wizards are in the same position. Data migration and refresh hand the automation op://OP_VAULT/fms-ear-key_<host>/password, and a template’s Encryption (EAR) preflight resolves that same host-wide entry in OP_VAULT — so it will report the key as missing even for a file whose passcode this workflow filed successfully.

Check whether a database has scheduled jobs before encrypting it in production. The wizard’s review step and the result page both say this, and name the entry to create.

   
Entry fms-ear-key_<host>_<hosted filename>
Vault OP_EAR_VAULT — a vault holding EAR keys and nothing else, separate from the one every other lookup reads. Not the vault the ansible roles read; see the warning below.
When After the result’s encryption header has been verified, before it is hosted
Runs that skip it A decrypt (no passcode to save) and any stage run (nothing is hosted, so no automation will meet the file)

The entry is a login item: the passcode is its password field (which is what the ansible roles read), its username is the database filename, and it is tagged fm-developer-portal, fms-ear-key and the hostname so the portal’s entries can be found as a set.

Its note carries the provenance someone needs when they open this entry months later because a job cannot open a database — what the key unlocks, where it came from, and whether it is current:

Database:        sales-01_encrypted.fmp12
Host:            fms-t02 (fms-t02.example.com)
Change:          Re-encrypted (passcode changed)
When:            2026-08-15 23:41 PDT
By:              someone@example.com
Shared ID:       fms-t02.example.com
Container data:  moved into secure containers
Source file:     /db/sales-01.fmp12
Portal run:      https://<portal>/encrypt/42/progress/
Portal run id:   #42

plus a short warning that the entry name is load-bearing — the roles look it up by exact name, so renaming it to tidy up will make automated jobs fail to open the file. The note holds no secret: not the passcode, not the hint, not the file account. Links are absolute when PORTAL_BASE_URL is set and a path otherwise.

The note is rewritten on a re-key rather than appended to, so it always describes the passcode currently in the entry; earlier versions are in the item’s own 1Password history. Tags added by hand are kept.

Two further details worth knowing:

  • It is the file-specific entry, not the host-wide one. The ansible roles resolve fms-ear-key_<host>_<file> first and fall back to fms-ear-key_<host> only when it is absent, so this is the name they will prefer once they are pointed at this vault — and it cannot clobber the host-wide key that every other encrypted database on that server may still depend on. An existing entry of the same name is updated in place rather than duplicated. The name is one the automation understands; the vault is not one it reads yet, so the warning above still applies.
  • A failed save does not fail the run. The file is correctly encrypted either way, and refusing to host it because a password manager was unreachable would be the bookkeeping breaking the thing it is bookkeeping for. Instead the result page says so in red and warns that no backup copy exists, so the operator’s own copy is the only one — tell DevOps, who can add it by hand.

The save happens before hosting on purpose. At that point the passcode is proven — a file verified as encrypted is a file that passcode opens — and if hosting then fails, a recorded key for a staged file is harmless. The other order risks a live encrypted database whose key the automation cannot find.

Why a separate vault

This is the only write the portal makes to 1Password; everything else it does there is a read. 1Password grants permissions per vault, not per item, so a service account that can write EAR keys into the main deployment vault could also overwrite everything else in it — ssh-key_*, fms-creds_*, fms-dmt-creds_*, duplicity-creds_*, which is to say the SSH private keys and admin credentials for the whole fleet. Handing that to a web application is a much larger risk than this convenience is worth, so the passcode goes to a vault holding EAR keys and nothing else.

A 1Password service account has exactly one token, so this needs a second service account: one that reads (OP_SERVICE_ACCOUNT_TOKEN, read on the main vault) and one that writes (OP_EAR_SERVICE_ACCOUNT_TOKEN, read and write on the EAR vault and no access to the main one). Neither can do the other’s job, so the credential that can write cannot also read the fleet’s SSH private keys.

Neither OP_EAR_VAULT nor OP_EAR_SERVICE_ACCOUNT_TOKEN falls back to its counterpart. Left unset, the backfill is simply off: runs still encrypt and host correctly and report that the passcode was not filed, which is the safe way to fail.

Setting this up is not just a portal change, and not just a permission change either. The portal and the ansible automation currently share one 1Password service account, and a 1Password grant attaches to the account rather than to whoever holds the token — so widening that account so “the portal can write” would mean “AWX can write too”. Hence the new service account the write uses: read and write on the EAR vault, and no access to the main vault, so the credential that can write is not also the one that reads the fleet’s SSH keys. The shared account keeps the access it already has and gains read on the EAR vault, and the roles’ EAR lookups have to search it: the onepassword role reads from a single explicitly named vault, so it will not find keys there on its own. See the README’s File Encryption and the EAR key vault for the full sequence.


What is recorded

A FileEncryptOperation row per run: the host, which change was made, the source path and name, the outcome, the hosted name, the shared ID, whether -keep_open_storage was passed, whether the replaced file was backed up, who ran it, and how long it took.

No passcode field exists on that model. What the history tells you is that encryption changed and who changed it — never the key.


Conflicts

An encryption run treats its source as a write, not a read: the file is closed for the duration, so anything else that reads or writes that name on that server is blocked while it runs, and vice versa. This is checked against every other operation type as well as its own — an encryption run and a data migration cannot touch the same file at once.


This site uses Just the Docs, a documentation theme for Jekyll.