diff --git a/dune/gdt/operators/advection-fv.hh b/dune/gdt/operators/advection-fv.hh
index e6e8655c78cadacade9698adcc853a50c61e9a2d..304626da509efe5e331ff2463a936daadc59f1d4 100644
--- a/dune/gdt/operators/advection-fv.hh
+++ b/dune/gdt/operators/advection-fv.hh
@@ -13,7 +13,9 @@
 #include <dune/xt/common/type_traits.hh>
 #include <dune/xt/grid/type_traits.hh>
 #include <dune/xt/grid/walker/filters.hh>
+#include <dune/xt/la/container.hh>
 
+#include <dune/gdt/local/assembler/operator-fd-jacobian-assemblers.hh>
 #include <dune/gdt/local/operators/advection-fv.hh>
 
 #include "interfaces.hh"
@@ -46,6 +48,7 @@ public:
   using BoundaryTreatmentByCustomExtrapolationOperatorType =
       LocalAdvectionFvBoundaryTreatmentByCustomExtrapolationOperator<I, V, SGV, m, F, F, RGV, V>;
 
+  using typename BaseType::MatrixOperatorType;
   using typename BaseType::SourceSpaceType;
   using typename BaseType::RangeSpaceType;
   using typename BaseType::VectorType;
@@ -149,6 +152,59 @@ public:
     DUNE_THROW_IF(!range.valid(), Exceptions::operator_error, "range contains inf or nan!");
   } // ... apply(...)
 
+  std::vector<std::string> jacobian_options() const override final
+  {
+    return {"finite-differences"};
+  }
+
+  XT::Common::Configuration jacobian_options(const std::string& type) const override final
+  {
+    DUNE_THROW_IF(type != this->jacobian_options().at(0), Exceptions::operator_error, "type = " << type);
+    return {{"type", type}, {"eps", "1e-7"}};
+  }
+
+  using BaseType::jacobian;
+
+  void jacobian(const VectorType& source,
+                MatrixOperatorType& jacobian_op,
+                const XT::Common::Configuration& opts,
+                const XT::Common::Parameter& param = {}) const override final
+  {
+    // some checks
+    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
+    DUNE_THROW_IF(!(this->parameter_type() <= param.type()),
+                  Exceptions::operator_error,
+                  "this->parameter_type() = " << this->parameter_type() << "\n   param.type() = " << param.type());
+    DUNE_THROW_IF(!opts.has_key("type"), Exceptions::operator_error, opts);
+    DUNE_THROW_IF(opts.get<std::string>("type") != jacobian_options().at(0), Exceptions::operator_error, opts);
+    const auto default_opts = jacobian_options(jacobian_options().at(0));
+    const auto eps = opts.get("eps", default_opts.template get<double>("eps"));
+    const auto parameter = param + XT::Common::Parameter({"finite-difference-jacobians.eps", eps});
+    // append the same local ops with the same filters as in apply() above
+    // contributions from inner intersections
+    jacobian_op.append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
+                       source,
+                       parameter,
+                       XT::Grid::ApplyOn::InnerIntersectionsOnce<SGV>());
+    // contributions from periodic boundaries
+    jacobian_op.append(LocalAdvectionFvCouplingOperator<I, V, SGV, m, F, F, RGV, V>(*numerical_flux_),
+                       source,
+                       parameter,
+                       *(XT::Grid::ApplyOn::PeriodicBoundaryIntersectionsOnce<SGV>() && !(*periodicity_exception_)));
+    // contributions from other boundaries by custom numerical flux
+    for (const auto& boundary_treatment : boundary_treatments_by_custom_numerical_flux_) {
+      const auto& boundary_op = *boundary_treatment.first;
+      const auto& filter = *boundary_treatment.second;
+      jacobian_op.append(boundary_op, source, parameter, filter);
+    }
+    // contributions from other boundaries by custom extrapolation
+    for (const auto& boundary_treatment : boundary_treatments_by_custom_extrapolation_) {
+      const auto& boundary_op = *boundary_treatment.first;
+      const auto& filter = *boundary_treatment.second;
+      jacobian_op.append(boundary_op, source, parameter, filter);
+    }
+  } // ... jacobian(...)
+
 private:
   const SGV assembly_grid_view_;
   const std::unique_ptr<const NumericalFluxType> numerical_flux_;