Firewall outages are rarely caused by hardware alone. More often, a well-intentioned change to a policy, an address object, or an SD-WAN rule goes live late on a Friday, nobody records exactly what was touched, and the engineer who remembers the original state has since moved on. In that moment, the quality of your FortiGate configuration backups decides whether recovery takes ten minutes or ten hours.
Oxidized, the open-source network configuration backup tool, addresses this problem directly. It logs in to each FortiGate over SSH on a schedule, collects the configuration, and commits every change to a Git repository. As a result, each modification becomes a commit with a timestamp, a diff, and a permanent history that outlives any individual administrator. This guide covers the complete implementation: preparing FortiOS, deploying Oxidized with Docker, tuning the FortiOS model for readable diffs, pushing to a remote repository, triggering backups from syslog, hardening the pipeline, and restoring a failed unit.
What Is Oxidized for FortiGate?
Oxidized for FortiGate is an automated backup workflow in which the Oxidized application connects to Fortinet FortiOS devices over SSH, runs the commands defined in its fortios model, stores the returned configuration text, and commits each changed version to a Git repository. It delivers scheduled backups, version history, diff views, and a REST API without requiring FortiManager or any commercial license.
Key Takeaways
- Oxidized ships with a native fortios model, so no custom scripting is needed to back up FortiGate firewalls.
- Git output turns every configuration change into an auditable commit, which supports change control, rollback planning, and compliance evidence.
- A dedicated read-only administrator with trusted hosts keeps the backup account low-risk.
- FortiOS configurations contain timestamps and encrypted secrets that create noisy diffs, so model variables deserve careful tuning.
- Syslog-triggered backups capture changes within seconds instead of waiting for the next polling interval.
- Backups are only valuable if you have rehearsed a restore, so test one before you need one.
Why FortiGate Configuration Backups Deserve Better Than Scheduled Exports
FortiGate administrators already have several ways to export a configuration. Nevertheless, most of those methods solve only part of the problem, and the gaps become obvious during an incident or an audit.
What Native FortiGate Backup Options Do Well
FortiOS provides a manual backup from the GUI, the execute backup config command family for TFTP, FTP, and SFTP targets, automatic revision backups on administrator logout, and centralized revision history through FortiManager. For a single firewall, these tools are perfectly adequate. In addition, FortiManager is excellent when you already manage policy packages centrally and license every device for it.
Where Native Options Fall Short
Problems appear at scale and in mixed environments. Manual exports depend on someone remembering to run them, scheduled scripts need maintenance, and local revision history on the device disappears along with the device itself. Furthermore, a FortiManager revision tells you that something changed, but it does not integrate naturally with the Git workflows, pull requests, and CI pipelines that many network teams now use.
| Approach | Strengths | Limitations |
|---|---|---|
| Manual GUI export | Simple, no setup | Human-dependent, no history, no alerting |
| Scripted execute backup config | Scheduled, uses native commands | Requires TFTP or SFTP server, weak diffing, script upkeep |
| Revision backup on logout | Automatic on the device | Stored locally, lost if the unit fails |
| FortiManager revisions | Central history, policy packages | Licensing and architecture overhead, Fortinet-only |
| Oxidized with Git | Free, versioned, multi-vendor, API-driven | Requires a server and operational ownership |
In practice, many mature teams run both FortiManager and Oxidized. FortiManager handles provisioning, while Oxidized acts as an independent, vendor-neutral record that also covers switches, routers, and non-Fortinet firewalls.
How Oxidized Works with FortiOS
Before building anything, it helps to understand what each moving part does. Oxidized is a Ruby application built around four pluggable concepts: sources, inputs, models, and outputs.
Architecture Components
| Layer | Component | Function in a FortiGate Deployment |
|---|---|---|
| Source | CSV, SQL, HTTP, or NetBox | Supplies the list of firewalls, IP addresses, and credentials |
| Input | SSH | Opens the CLI session to FortiOS |
| Model | fortios | Defines which commands run and how output is cleaned |
| Output | Git or gitcrypt | Stores each changed configuration as a commit |
| Hooks | githubrepo, exec, slackdiff | Reacts to events such as a stored change |
| Interface | REST API and Oxidized Web | Exposes node status, diffs, and manual triggers |
The Backup Lifecycle
Each polling cycle follows the same sequence, which makes troubleshooting far more predictable once you recognize it.
- Oxidized reads the node list from the configured source.
- A worker thread opens an SSH session to the FortiGate using the credentials supplied for that node.
- The fortios model sends its commands and collects the output.
- Model rules strip volatile lines and, optionally, secrets.
- Oxidized compares the cleaned result with the last stored version.
- If the content differs, the output driver writes a new Git commit.
- Any hook bound to the post-store event fires, for example a push to a remote repository.
- The node is rescheduled for the next interval.
The FortiOS Model Explained
The fortios model gathers the system status, the hardware inventory, and the configuration itself, then removes elements that change on every run. Moreover, it recognizes the FortiOS prompt, which is where many custom setups fail. Because the model is community-maintained, new FortiOS releases occasionally require a model update, so keep the Oxidized image current rather than pinning it for years.
Prerequisites and Planning
A short planning session prevents most deployment problems. The table below summarizes what you need before touching the firewall.
| Requirement | Recommendation | Why It Matters |
|---|---|---|
| Backup server | Linux VM with 2 vCPU and 2 GB RAM for small estates | Oxidized is light, but Git history grows steadily |
| Container runtime | Docker Engine with Compose | Simplifies upgrades and rollback |
| FortiOS access | SSH enabled on a management interface | Oxidized uses the CLI, not the REST API of the firewall |
| Service account | Dedicated read-only administrator | Limits blast radius if credentials leak |
| Network path | TCP 22 from backup server to each FortiGate | Often blocked by local-in policies |
| Time sync | NTP on server and firewalls | Commit timestamps and syslog correlation depend on it |
| Remote Git | Private repository on GitLab, GitHub, or Gitea | Protects history if the backup server is lost |
Network and Firewall Considerations
FortiGate protects its own management plane with local-in policies and per-interface allowed access settings. Consequently, a backup that works from your laptop may fail from the Oxidized server simply because the server sits in a different subnet. Confirm that SSH is permitted on the interface Oxidized will reach, and that no local-in policy or trusted host entry drops the session.
Sizing for Larger Estates
Oxidized scales comfortably into the thousands of devices when you raise the thread count and tune timeouts, yet the real constraint is usually the firewalls themselves. A busy FortiGate may take several seconds to render a large configuration, so avoid setting a thread count that launches hundreds of simultaneous sessions against one site over a slow WAN link.
Preparing the FortiGate for Oxidized
The goal is a service account that can read the configuration and nothing else. Doing this properly takes about five minutes per firewall, and it is the single most valuable security decision in the project.
Create a Least-Privilege Admin Profile
Start by defining an access profile with read permissions. FortiOS also ships with a built-in read-only super administrator profile on recent versions, which is convenient for multi-VDOM units. However, a custom profile makes the intent explicit and survives upgrades without surprises.
config system accprofile edit "oxidized-ro" set secfabgrp read set ftviewgrp read set authgrp read set sysgrp read set netgrp read set loggrp read set fwgrp read set vpngrp read set utmgrp read set wanoptgrp read set wifi read next end The profile above grants read access across the major permission groups. Note that available groups differ between FortiOS releases, so run set ? inside the edit context and adjust to match your version.
Create the Service Account with Trusted Hosts
Next, create the administrator and bind it to the Oxidized server address. The trusted host setting means that even a stolen password is useless from any other network location.
config system admin edit "oxidized" set accprofile "oxidized-ro" set vdom "root" set trusthost1 10.10.50.25 255.255.255.255 set password ChangeThisLongRandomPassword next end After saving, verify the account by opening an SSH session from the Oxidized server and running show full-configuration. If the output is truncated or secrets appear unexpectedly masked, revisit the profile permissions before proceeding.
Use SSH Key Authentication Where Possible
Passwords work, but keys are better. A dedicated key pair removes the password from your Oxidized configuration file and makes rotation straightforward. FortiOS accepts a public key on the administrator object.
config system admin edit "oxidized" set ssh-public-key1 "ssh-rsa AAAAB3NzaC1yc2E... oxidized@backup01" next end On the Oxidized side, the key path is supplied through the ssh_keys variable, which you will see in the configuration section. Because key type support varies between FortiOS versions, test RSA first if an ed25519 key is rejected.
Multi-VDOM Considerations
When virtual domains are enabled, the global configuration and each VDOM configuration are separate scopes. To capture everything in a single file, the backup account needs sufficient privileges at the global level. In contrast, a VDOM-scoped administrator only sees its own VDOM, which is useful for MSP tenants but insufficient for a full device backup. Test your chosen profile in a lab unit, because the model’s behavior with multi-VDOM units can differ across Oxidized releases.
FortiGate High Availability Clusters
In an active-passive cluster, configuration synchronizes from the primary to the secondary, so backing up the cluster address normally captures the full configuration. Even so, node-specific values such as hostnames and reserved management interface addresses can differ. For that reason, many teams register each member’s dedicated management address as a separate Oxidized node, which also confirms that both units remain reachable.
Installing Oxidized with Docker
Docker is the most maintainable way to run Oxidized because upgrading means pulling a new image rather than wrestling with Ruby gems and native libraries such as libssh and libgit2.
Directory Layout
Create a persistent directory on the host. This single directory will hold the configuration file, the inventory, and the Git repository, so it is the one location you must back up.
sudo mkdir -p /srv/oxidized sudo chown -R 30000:30000 /srv/oxidized The official image runs as an unprivileged user, and ownership mismatches are a leading cause of the container exiting immediately. Check the image documentation for the UID used by your version.
The Core Configuration File
Save the following as /srv/oxidized/config. It defines polling behavior, SSH input, Git output, a CSV source, and the REST interface bound to localhost.
username: oxidized password: ChangeThisLongRandomPassword model: fortios interval: 3600 use_syslog: false debug: false threads: 20 timeout: 30 retries: 3 rest: 127.0.0.1:8888 vars: remove_secret: false ssh_keys: /home/oxidized/.config/oxidized/ssh/id_rsa input: default: ssh ssh: secure: false output: default: git git: user: Oxidized email: oxidized@example.com repo: /home/oxidized/.config/oxidized/fortigate.git source: default: csv csv: file: /home/oxidized/.config/oxidized/router.db delimiter: !ruby/regexp /:/ map: name: 0 ip: 1 model: 2 group: 3 Several choices here deserve explanation. The interval of 3600 seconds polls hourly, which balances freshness against load. Setting secure: false skips SSH host key verification, which is convenient in a lab, though production deployments should enable it and pre-populate the known hosts file to prevent man-in-the-middle exposure. Finally, binding the REST interface to localhost keeps an unauthenticated API off the network until you place a reverse proxy in front of it.
The Device Inventory
The CSV source reads one firewall per line using the colon delimiter and the field map above.
fw-hq-01:10.10.1.1:fortios:edge fw-branch-02:10.20.1.1:fortios:branch fw-dc-03:10.30.1.1:fortios:datacenter The node name should match the hostname configured on the FortiGate, because that alignment becomes essential when you add syslog triggers later. For larger estates, replace the CSV file with a NetBox or HTTP source so that the inventory is generated rather than hand-edited.
Starting the Container
docker run -d --name oxidized \ --restart unless-stopped \ -p 127.0.0.1:8888:8888 \ -v /srv/oxidized:/home/oxidized/.config/oxidized \ oxidized/oxidized:latest oxidized Watch the first run with docker logs -f oxidized. A healthy start shows each node being queued and, after a short delay, a message indicating that the configuration was stored.
Verifying the First Backup
Once the logs look clean, confirm through the API and the repository.
curl -s http://127.0.0.1:8888/nodes.json git -C /srv/oxidized/fortigate.git log --oneline The first command lists every node with its last status, while the second shows the commit history. If a node reports a failure, jump ahead to the troubleshooting section, where the common causes are tabulated.
Git Versioning for FortiGate Configurations
Git is what elevates Oxidized from a backup utility to a change-tracking system. Each time a configuration differs from the last stored copy, Oxidized writes a commit, which gives you a precise record of what changed and when.
Why Git Suits Firewall Configurations
FortiOS configurations are structured, line-oriented text, which is the ideal input for diff algorithms. Consequently, a one-line change to a firewall policy produces a one-line diff, and a change in the middle of a six-thousand-line file remains easy to spot. Additionally, Git provides cryptographic integrity of history, cheap storage through delta compression, and a universal toolset that auditors and engineers already understand.
Git Output Options
| Output | Use Case | Consideration |
|---|---|---|
| git | Standard versioned backups | Repository stored in plain text on the server |
| gitcrypt | Encrypted repositories | Requires git-crypt setup and key management |
| file | Simple flat files | No history, so unsuitable for change tracking |
| http | Forwarding to another system | Needs a receiving service you maintain |
For firewall configurations, choose git at minimum, and consider gitcrypt if the repository will be mirrored to a shared platform.
Reading the History
Because Oxidized writes ordinary Git commits, standard commands work. The repository is bare by convention, so use the -C flag or clone it.
# Clone for convenient browsing git clone /srv/oxidized/fortigate.git ~/fortigate-configs cd ~/fortigate-configs # Show the last five changes to one firewall git log -5 --stat -- fw-hq-01 # Show exactly what changed in the most recent commit git show HEAD -- fw-hq-01 # Compare two points in time git diff HEAD~10 HEAD -- fw-hq-01 In a repository organized by group, files appear under their group directory, so adjust paths to match. Importantly, commit messages record the node and group names, which makes git log --grep a quick way to find every change to a specific firewall.
Pushing to a Remote Repository
A backup that lives only on the backup server is not really a backup. Therefore, add a hook that pushes each stored change to a private remote.
hooks: push_to_remote: type: githubrepo events: [post_store] remote_repo: git@git.example.com:netops/fortigate-configs.git publickey: /home/oxidized/.config/oxidized/ssh/deploy.pub privatekey: /home/oxidized/.config/oxidized/ssh/deploy Despite the hook’s name, it pushes to any Git server that accepts SSH, including GitLab and Gitea. Use a deploy key restricted to this single repository with write access, and protect the main branch so that nobody rewrites history.
Taming Noisy Diffs in FortiOS Backups
The most common complaint about FortiGate versioning is noise. If every poll produces a commit, the history becomes useless. Fortunately, the causes are well understood.
| Noise Source | What Happens | Remedy |
|---|---|---|
| Config header timestamp | Header line changes on each export | Enable the model’s timestamp removal variable if your version provides it |
| Encrypted secrets | Some ENC values are re-salted between exports | Use remove_secret, accepting that restores then need secrets re-entered |
| Uptime or counters in status output | Volatile values change constantly | Rely on the model’s built-in filtering and update Oxidized |
| Full versus partial config | Defaults inflate the file and shift with upgrades | Choose the export style your team prefers and keep it consistent |
The trade-off around secrets is worth pausing on. Removing secrets produces tidy diffs and a repository that is safer to share, but a backup stripped of pre-shared keys and local passwords cannot fully restore a firewall unaided. Conversely, keeping secrets yields a complete backup that must be protected like a credential vault. Most teams keep secrets on the backup server, encrypt the repository, and never mirror it to a public platform.
Because model variable names evolve, check the fortios model documentation for your installed version before relying on any specific option. Then, after changing a variable, trigger a manual run and confirm that a second consecutive backup produces no new commit. That simple test proves your noise suppression works.
Real-Time Backups with Syslog Triggers
Hourly polling is a sensible baseline, yet it leaves a window in which a change exists on the firewall but not in Git. If that window matters to your change process, you can close it by letting the FortiGate announce its own configuration changes.
How the Trigger Works
FortiOS writes an event log entry whenever an administrator edits an object or attribute. When those events are forwarded to a syslog receiver, a small script can extract the device name and call the Oxidized REST endpoint that moves that node to the front of the queue. Consequently, the backup runs seconds after the change rather than at the next interval.
- An administrator saves a change on the FortiGate.
- FortiOS generates a configuration event and sends it to the syslog server.
- rsyslog passes each matching line to a trigger script.
- The script extracts the device name from the log line.
- The script calls the Oxidized API to schedule that node immediately.
- Oxidized fetches the configuration and commits the diff to Git.
Configure FortiOS to Send Syslog
config log syslogd setting set status enable set server "10.10.50.25" set port 514 set facility local7 end This forwards logs to the Oxidized host. Make sure the log filter includes event logs, and that the system event subtype for configuration changes is enabled in your logging settings.
Configure rsyslog and the Trigger Script
On the backup server, enable a UDP listener and hand each message to a program.
module(load="imudp") input(type="imudp" port="514" ruleset="fgt") ruleset(name="fgt") { action(type="omprog" binary="/usr/local/bin/oxidized-trigger.sh") } The script below looks for the configuration-change log identifiers that FortiOS commonly uses for attribute and object edits. Verify the identifiers against the log reference for your FortiOS release, since they can vary.
#!/bin/bash pattern='devname="([^"]+)"' while read -r line; do case "$line" in *'logid="0100044546"'*|*'logid="0100044547"'*) if [[ $line =~ $pattern ]]; then curl -s "http://127.0.0.1:8888/node/next/${BASH_REMATCH[1]}" > /dev/null fi ;; esac done Because the script uses the devname field, the node name in your Oxidized inventory must match the FortiGate hostname exactly. Moreover, a single administrative session can generate dozens of log lines, so you may prefer to add a short debounce, for example by writing the device name to a temporary file and processing it once every thirty seconds. Finally, restrict UDP 514 to your firewall addresses, because an open syslog listener can be abused to trigger needless backups.
Using the Oxidized REST API and Web Interface
Oxidized exposes a lightweight HTTP interface that is useful for dashboards, scripts, and manual operations. The same service provides a web view of nodes, versions, and diffs.
| Endpoint | Purpose |
|---|---|
| /nodes | Lists all nodes with status and last run time |
| /nodes.json | Same data in JSON for automation |
| /node/fetch/NAME | Returns the latest stored configuration |
| /node/next/NAME | Queues an immediate backup for that node |
| /node/version?node_full=GROUP/NAME | Lists stored versions of a node |
| /reload | Reloads the inventory from the source |
For example, to force a fresh backup of one firewall and then read it back, run:
curl -s http://127.0.0.1:8888/node/next/fw-hq-01 curl -s http://127.0.0.1:8888/node/fetch/fw-hq-01 | head -40 In practice, the JSON node list is a convenient data source for monitoring. A Prometheus exporter or a simple script can raise an alert when any node reports a status other than success for more than two intervals, which catches silent failures such as an expired password or a changed trusted host.
Securing the Backup Pipeline
A repository of firewall configurations is a high-value target. It reveals your network topology, address objects, VPN peers, and, depending on your settings, secrets. Treat the Oxidized server accordingly.
| Control | Recommendation |
|---|---|
| Service account | Read-only profile, single trusted host, unique strong credential or key |
| Transport | SSH only, with host key verification enabled in production |
| Secrets in Git | Encrypt with gitcrypt or strip with remove_secret, depending on restore needs |
| API exposure | Bind to localhost and publish through a reverse proxy with authentication and TLS |
| Server hardening | Patch regularly, limit SSH access, enable disk encryption |
| Remote repository | Private, deploy key per repository, protected main branch, MFA for human users |
| Credential storage | Keep passwords out of version control, prefer SSH keys and environment secrets |
| Monitoring | Alert on failed nodes and on unexpected commits |
These measures align with widely used frameworks. For instance, NIST SP 800-53 addresses configuration baselines and change control, while the CIS Controls call for secure configuration of network devices. For a deeper, tool-specific checklist, the dedicated security guide for Oxidized covers hardening in more detail.
Restoring a FortiGate from an Oxidized Backup
A backup earns its value during recovery, so rehearse this procedure in a lab before an emergency forces you to improvise.
Restore Workflow
- Identify the correct commit using
git logand confirm it withgit diffagainst the current state. - Retrieve the file with
git show COMMIT:PATHand save it as a text file. - Remove any leading comment lines that Oxidized added from command output, so the file begins at the FortiOS configuration header.
- Confirm that the firmware version on the target matches the version recorded in the header.
- Make the file available on a TFTP, FTP, or SFTP server, or upload it through the GUI restore option.
- Run the restore, accept the reboot, and verify connectivity and policy behavior afterward.
git show 3f9a2c1:fw-hq-01 > fw-hq-01.conf From the FortiGate CLI, a restore from a file server looks like this, with the exact protocol options depending on your firmware:
execute restore config sftp fw-hq-01.conf 10.10.50.25 backupuser Restore Pitfalls to Know About
Several issues catch teams out. First, FortiOS validates the configuration header, so removing the timestamp line for cleaner diffs can interfere with a restore unless you re-add a valid header. Second, if you stripped secrets, local administrator passwords and pre-shared keys must be re-entered. Third, restoring a configuration from a different firmware generation can fail or silently drop unsupported settings. Therefore, keep a note of the FortiOS version alongside each backup, which the model’s status output usually captures.
For partial rollbacks, a safer technique is to copy only the affected CLI blocks, such as a single config firewall policy section, and paste them into a console session during a maintenance window.
Change Auditing and Compliance Evidence
Because every commit records what changed and when, Git history doubles as audit evidence. Pairing it with FortiGate event logs, which record who made the change, produces a complete trail.
| Framework | Relevant Area | How Oxidized Helps |
|---|---|---|
| NIST SP 800-53 | CM-2, CM-3, CM-6 | Baseline configurations, change tracking, configuration settings |
| CIS Controls | Secure configuration of network devices | Versioned, reviewable configurations with drift visibility |
| ISO 27001:2022 | Configuration management | Documented, retrievable configuration history |
| PCI DSS 4.0 | Network security control configuration reviews | Diffs that simplify periodic configuration review |
| SOC 2 | Change management criteria | Timestamped records of production changes |
To operationalize this, schedule a weekly job that emails the diff summary of all firewall changes to the security team, and require that every commit correlate with an approved change ticket. As a result, unauthorized or emergency changes become visible within hours rather than at the next audit.
Scaling Oxidized Across Many FortiGates
As the estate grows, three techniques keep the deployment manageable.
Use Groups for Different Credentials and Sites
Groups allow separate credentials or variables per environment, which suits datacenters, branches, and customer tenants.
groups: datacenter: username: oxidized-dc password: AnotherLongRandomPassword branch: username: oxidized-br password: YetAnotherLongRandomPassword Each node picks up its group from the inventory field mapped earlier, and Git stores its file under the group directory. For MSPs, this separation also prevents one customer’s credentials from being used against another’s firewall.
Generate the Inventory from a Source of Truth
Maintaining a CSV by hand works for twenty firewalls but becomes error-prone at two hundred. Switching the source to NetBox or an HTTP endpoint means that adding a device to your documentation automatically adds it to the backup rotation. Likewise, decommissioning a device removes it, which keeps stale failures out of your dashboard.
Tune Threads, Timeouts, and Intervals
Start with 20 threads, a 30-second timeout, and a one-hour interval. Then watch the node list. If many nodes time out on slow links, raise the timeout before raising retries. Conversely, if the server is idle and the queue is long, increase threads gradually while observing CPU usage on both the server and the busiest firewalls.
Troubleshooting FortiGate Backups in Oxidized
Most failures fall into a handful of categories. Start by reading the container log, then reproduce the connection manually from the same host using SSH.
| Symptom | Likely Cause | Fix |
|---|---|---|
| Node shows no_connection | Trusted host, local-in policy, or routing blocks SSH | Test with ssh from the Oxidized host and adjust access settings |
| Authentication failed | Wrong credential, locked account, or expired password | Verify the login manually and review admin lockout settings |
| Timeout during fetch | Slow link or very large configuration | Raise the timeout and reduce concurrent threads |
| Output truncated | Paging prompt or incomplete permissions | Check the console output setting and the access profile |
| Key exchange or cipher error | Old SSH library versus modern FortiOS ciphers | Update the Oxidized image and check allowed algorithms |
| Commit on every run | Volatile lines in output | Tune model variables and update Oxidized |
| Container exits at start | File ownership or YAML syntax error | Fix permissions and validate the YAML |
| Missing group directory | Group field not mapped | Correct the source map indexes |
Debugging a Single Node
When one firewall misbehaves, isolate it. Temporarily enable debug in the configuration, restart, and watch the SSH session. Alternatively, test the same login with a plain SSH client using the identical account. If the manual login works but Oxidized fails, the cause is usually a prompt mismatch, which happens when a customized login banner or an unusual hostname character confuses the prompt matching.
When FortiOS Upgrades Break Backups
Occasionally, a major FortiOS release changes banner text, prompt behavior, or cipher support. Consequently, backups may begin failing after an upgrade even though nothing changed on the Oxidized side. Pull the latest image, retry, and if the problem persists, open an issue in the Oxidized project with the FortiOS version and a sanitized debug log. Because the model is community-maintained, such reports are usually resolved quickly.
Common Mistakes to Avoid
- Using a full administrator account for backups, which turns a convenience into a liability.
- Skipping trusted hosts, so the credential works from anywhere.
- Leaving the REST API listening on all interfaces without authentication.
- Storing the only copy of the repository on the backup server.
- Ignoring failed nodes until the day a restore is needed.
- Mismatched node names and firewall hostnames, which break syslog-triggered runs.
- Never testing a restore, then discovering that the stored file needs editing.
- Disabling host key verification permanently after a lab trial.
- Running with the latest tag unattended, then losing a working version after a surprise update.
Oxidized Compared with Other FortiGate Backup Options
| Tool | Cost | Version Control | Multi-Vendor | Best Fit |
|---|---|---|---|---|
| Oxidized | Free | Native Git | Yes | Teams that want Git workflows and API access |
| FortiManager | Licensed | Revision history | Fortinet only | Central policy management and provisioning |
| RANCID | Free | CVS or Subversion style | Yes | Legacy environments already using it |
| Netmiko or Nornir scripts | Free | You build it | Yes | Teams needing custom logic and writing code |
| Commercial NCM suites | Licensed | Yes | Yes | Organizations wanting vendor support and reporting |
When the decision is between Oxidized and RANCID, the usual deciding factors are Git support, an active community, and a maintained REST API, all of which favor Oxidized. Scripts built with Netmiko or Nornir offer more flexibility, but they also require you to own scheduling, error handling, and diffing.
Real-World Scenarios
Scenario One: Recovering from a Bad Policy Push
A branch firewall loses connectivity after a late change to an SD-WAN rule. Without history, the team would compare against memory. With Oxidized, an engineer runs git log for the node, spots the commit from twenty minutes earlier, reads the diff, and reverts the single block. Total recovery time is a few minutes, and the incident review has an exact record.
Scenario Two: Replacing a Failed Unit
A datacenter FortiGate suffers a hardware fault. The replacement is staged on the bench, upgraded to the recorded firmware version, and restored from the latest Oxidized commit after the leading comment lines are removed. Secrets were retained in the encrypted repository, so VPN tunnels return without manual re-keying.
Scenario Three: Passing an Audit
During a PCI DSS assessment, the assessor requests evidence of review of firewall configurations. The team exports the Git log for the previous quarter, matches commits to approved change tickets, and highlights the single emergency change that was retroactively documented. The conversation shifts from skepticism to confirmation.
Optimization Recommendations
| Area | Recommendation | Benefit |
|---|---|---|
| Image management | Pin a tested version tag and upgrade deliberately | Predictable behavior |
| Polling | Hourly interval plus syslog triggers | Fresh data with low load |
| Repository | Periodic garbage collection and off-site mirror | Smaller storage, resilience |
| Alerting | Notify on failed nodes and on commits to critical firewalls | Faster response |
| Documentation | Store a README in the repository describing restore steps | Faster recovery under pressure |
| Access | Rotate keys and credentials on a schedule | Reduced exposure |
Frequently Asked Questions
Does Oxidized support FortiGate firewalls?
Yes. Oxidized includes a fortios model that supports FortiGate devices running FortiOS, connecting over SSH and storing the configuration in your chosen output, typically Git.
Do I need FortiManager to use Oxidized?
No. Oxidized connects directly to each FortiGate and works independently of FortiManager, although the two can coexist.
What permissions does the Oxidized account need on FortiOS?
The account needs read access sufficient to display the full configuration. A dedicated read-only administrator profile with a trusted host restriction is the recommended approach.
How often should I back up FortiGate configurations?
An hourly poll suits most environments. For tighter control, combine polling with syslog-triggered backups so that changes are captured within seconds.
Does Oxidized back up FortiGate VDOM configurations?
It can, provided the account has global access on a multi-VDOM unit. Test against a lab device, because behavior can vary between Oxidized and FortiOS versions.
Are secrets stored in the Oxidized backup?
By default, encrypted secrets remain in the stored configuration. You can strip them with the remove_secret option, but a backup without secrets cannot fully restore a firewall.
Can Oxidized restore a configuration to a FortiGate?
Oxidized only backs up and versions configurations. Restoration is performed with FortiOS tools such as the GUI restore function or the execute restore config command.
How do I push FortiGate backups to GitHub or GitLab?
Use the githubrepo hook with a deploy key and a private remote repository, bound to the post_store event so that each change is pushed automatically.
Why does Oxidized create a commit every run?
Volatile lines such as timestamps usually cause this. Update Oxidized, enable the relevant model variables, and confirm that two consecutive runs produce no commit.
Is Oxidized suitable for production firewalls?
Yes, provided you use a least-privilege account, protect the repository, monitor failures, and rehearse restores. Many organizations rely on it daily.
Conclusion
Automating FortiGate backups with Oxidized and Git replaces guesswork with evidence. A read-only service account keeps risk low, the fortios model removes most of the manual effort, and Git supplies the history that change management, audits, and incident response all depend on. Furthermore, syslog triggers and the REST API turn a simple scheduled job into a responsive part of your operational toolkit.
The practical next step is modest: build the service account on one firewall, run the container, and confirm that two consecutive backups produce a single commit. From there, add the remote push, enable syslog triggers, and rehearse a restore. Once

