diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex
index ba01ed23a..72656e1d7 100644
--- a/lib/mobilizon/events/events.ex
+++ b/lib/mobilizon/events/events.ex
@@ -28,6 +28,7 @@ defmodule Mobilizon.Events do
     Track
   }
 
+  alias Mobilizon.Service.Export.Cachable
   alias Mobilizon.Service.Workers.BuildSearch
   alias Mobilizon.Service.Workers.EventDelayedNotificationWorker
   alias Mobilizon.Share
@@ -301,7 +302,7 @@ defmodule Mobilizon.Events do
            end)
            |> Repo.transaction(),
          %Event{} = new_event <- Repo.preload(new_event, @event_preloads, force: true) do
-      Cachex.del(:ics, "event_#{new_event.uuid}")
+      Cachable.clear_all_caches(new_event)
 
       unless new_event.draft do
         %{
@@ -355,14 +356,10 @@ defmodule Mobilizon.Events do
   Deletes an event.
   """
   @spec delete_event(Event.t()) :: {:ok, Event.t()} | {:error, Changeset.t()}
-  def delete_event(%Event{} = event), do: Repo.delete(event)
-
-  @doc """
-  Deletes an event.
-  Raises an exception if it fails.
-  """
-  @spec delete_event!(Event.t()) :: Event.t()
-  def delete_event!(%Event{} = event), do: Repo.delete!(event)
+  def delete_event(%Event{} = event) do
+    Cachable.clear_all_caches(event)
+    Repo.delete(event)
+  end
 
   @doc """
   Returns the list of events.
diff --git a/lib/mobilizon/posts/posts.ex b/lib/mobilizon/posts/posts.ex
index 716379bf9..28271b1e7 100644
--- a/lib/mobilizon/posts/posts.ex
+++ b/lib/mobilizon/posts/posts.ex
@@ -5,6 +5,7 @@ defmodule Mobilizon.Posts do
   alias Mobilizon.Actors.Actor
   alias Mobilizon.Events.Tag
   alias Mobilizon.Posts.Post
+  alias Mobilizon.Service.Export.Cachable
   alias Mobilizon.Storage.{Page, Repo}
 
   import Ecto.Query
@@ -107,6 +108,8 @@ defmodule Mobilizon.Posts do
   """
   @spec update_post(Post.t(), map) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
   def update_post(%Post{} = post, attrs) do
+    Cachable.clear_all_caches(post)
+
     post
     |> Repo.preload([:tags, :media])
     |> Post.changeset(attrs)
@@ -117,7 +120,10 @@ defmodule Mobilizon.Posts do
   Deletes a post
   """
   @spec delete_post(Post.t()) :: {:ok, Post.t()} | {:error, Ecto.Changeset.t()}
-  def delete_post(%Post{} = post), do: Repo.delete(post)
+  def delete_post(%Post{} = post) do
+    Cachable.clear_all_caches(post)
+    Repo.delete(post)
+  end
 
   @doc """
   Returns the list of tags for the post.
diff --git a/lib/service/actor_suspension.ex b/lib/service/actor_suspension.ex
index 4973208d0..1b6272a67 100644
--- a/lib/service/actor_suspension.ex
+++ b/lib/service/actor_suspension.ex
@@ -11,6 +11,7 @@ defmodule Mobilizon.Service.ActorSuspension do
   alias Mobilizon.Medias.File
   alias Mobilizon.Posts.Post
   alias Mobilizon.Resources.Resource
+  alias Mobilizon.Service.Export.Cachable
   alias Mobilizon.Storage.Repo
   alias Mobilizon.Users.User
   alias Mobilizon.Web.Email.Actor, as: ActorEmail
@@ -66,6 +67,7 @@ defmodule Mobilizon.Service.ActorSuspension do
     case Repo.transaction(multi) do
       {:ok, %{actor: %Actor{} = actor}} ->
         {:ok, true} = Cachex.del(:activity_pub, "actor_#{actor.preferred_username}")
+        Cachable.clear_all_caches(actor)
         Logger.info("Deleted actor #{actor.url}")
         {:ok, actor}
 
diff --git a/lib/service/export/cachable.ex b/lib/service/export/cachable.ex
new file mode 100644
index 000000000..4558de9ce
--- /dev/null
+++ b/lib/service/export/cachable.ex
@@ -0,0 +1,23 @@
+defmodule Mobilizon.Service.Export.Cachable do
+  @moduledoc """
+  Behavior that export modules that use caching should implement
+  """
+
+  alias Mobilizon.Actors.Actor
+  alias Mobilizon.Events.Event
+  alias Mobilizon.Posts.Post
+  alias Mobilizon.Service.Export.{Feed, ICalendar}
+
+  @callback create_cache(String.t()) :: any()
+
+  @callback clear_caches(Event.t() | Post.t() | Actor.t()) :: any()
+
+  @spec clear_all_caches(%{
+          :__struct__ => Mobilizon.Actors.Actor | Mobilizon.Events.Event | Mobilizon.Posts.Post,
+          optional(any) => any
+        }) :: {:error, boolean} | {:ok, boolean}
+  def clear_all_caches(entity) do
+    Feed.clear_caches(entity)
+    ICalendar.clear_caches(entity)
+  end
+end
diff --git a/lib/service/export/feed.ex b/lib/service/export/feed.ex
index 106d0a320..96f5644af 100644
--- a/lib/service/export/feed.ex
+++ b/lib/service/export/feed.ex
@@ -11,7 +11,7 @@ defmodule Mobilizon.Service.Export.Feed do
   alias Mobilizon.Config
   alias Mobilizon.Events.Event
   alias Mobilizon.Posts.Post
-  alias Mobilizon.Service.Export.Common
+  alias Mobilizon.Service.Export.{Cachable, Common}
   alias Mobilizon.Users.User
 
   alias Mobilizon.Web.Endpoint
@@ -19,11 +19,14 @@ defmodule Mobilizon.Service.Export.Feed do
 
   require Logger
 
+  @behaviour Cachable
+
   @item_limit 500
 
   @spec version :: String.t()
   defp version, do: Config.instance_version()
 
+  @impl Cachable
   @spec create_cache(String.t()) ::
           {:commit, String.t()}
           | {:ignore, :actor_not_found | :actor_not_public | :bad_token | :token_not_found}
@@ -37,6 +40,7 @@ defmodule Mobilizon.Service.Export.Feed do
     end
   end
 
+  @impl Cachable
   def create_cache("token_" <> token) do
     case fetch_events_from_token(token) do
       {:ok, res} ->
@@ -47,6 +51,7 @@ defmodule Mobilizon.Service.Export.Feed do
     end
   end
 
+  @impl Cachable
   def create_cache("instance") do
     {:ok, res} = fetch_instance_feed()
     {:commit, res}
@@ -227,4 +232,44 @@ defmodule Mobilizon.Service.Export.Feed do
     |> Feed.build()
     |> Atomex.generate_document()
   end
+
+  @impl Cachable
+  def clear_caches(%Event{attributed_to: %Actor{} = actor} = event) do
+    clear_actor_feed(actor)
+    clear_caches(%{event | attributed_to: nil})
+  end
+
+  @impl Cachable
+  def clear_caches(%Event{}) do
+    # TODO: It would be nice to clear feed token cache based on participations as well,
+    # but that's harder, as it would require loading all participations
+    clear_instance()
+  end
+
+  @impl Cachable
+  def clear_caches(%Post{attributed_to: %Actor{} = actor} = post) do
+    clear_actor_feed(actor)
+    clear_caches(%{post | attributed_to: nil})
+  end
+
+  @impl Cachable
+  def clear_caches(%Post{}) do
+    clear_instance()
+  end
+
+  @impl Cachable
+  def clear_caches(%Actor{} = actor) do
+    clear_actor_feed(actor)
+    clear_instance()
+  end
+
+  defp clear_instance do
+    Cachex.del(:feed, "instance")
+  end
+
+  defp clear_actor_feed(%Actor{preferred_username: preferred_username} = actor) do
+    if Actor.is_public_visibility?(actor) do
+      Cachex.del(:feed, "actor_#{preferred_username}")
+    end
+  end
 end
diff --git a/lib/service/export/icalendar.ex b/lib/service/export/icalendar.ex
index 7ba32cc48..5bcd67401 100644
--- a/lib/service/export/icalendar.ex
+++ b/lib/service/export/icalendar.ex
@@ -7,14 +7,17 @@ defmodule Mobilizon.Service.Export.ICalendar do
   alias Mobilizon.Addresses.Address
   alias Mobilizon.{Config, Events}
   alias Mobilizon.Events.{Event, EventOptions}
-  alias Mobilizon.Service.Export.Common
+  alias Mobilizon.Service.Export.{Cachable, Common}
   alias Mobilizon.Service.Formatter.HTML
 
+  @behaviour Cachable
+
   @item_limit 500
 
   @doc """
   Create cache for an actor, an event or an user token
   """
+  @impl Cachable
   @spec create_cache(String.t()) :: {:commit, String.t()} | {:ignore, atom()}
   def create_cache("actor_" <> name) do
     case export_public_actor(name) do
@@ -26,6 +29,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
     end
   end
 
+  @impl Cachable
   def create_cache("event_" <> uuid) do
     with %Event{} = event <- Events.get_public_event_by_uuid_with_preload(uuid),
          {:ok, res} <- export_public_event(event) do
@@ -39,6 +43,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
     end
   end
 
+  @impl Cachable
   def create_cache("token_" <> token) do
     case fetch_events_from_token(token) do
       {:ok, res} ->
@@ -49,6 +54,7 @@ defmodule Mobilizon.Service.Export.ICalendar do
     end
   end
 
+  @impl Cachable
   def create_cache("instance") do
     {:ok, res} = fetch_instance_feed()
     {:commit, res}
@@ -172,4 +178,45 @@ defmodule Mobilizon.Service.Export.ICalendar do
   defp organizer(%Event{organizer_actor: %Actor{} = profile}) do
     Actor.display_name(profile)
   end
+
+  @impl Cachable
+  @spec clear_caches(%{
+          :__struct__ => Mobilizon.Actors.Actor | Mobilizon.Events.Event | Mobilizon.Posts.Post,
+          optional(any) => any
+        }) :: {:error, boolean} | {:ok, boolean}
+  def clear_caches(%Event{attributed_to: %Actor{} = actor} = event) do
+    clear_actor_feed(actor)
+    clear_caches(%{event | attributed_to: nil})
+  end
+
+  @impl Cachable
+  def clear_caches(%Event{uuid: uuid}) do
+    # TODO: It would be nice to clear feed token cache based on participations as well,
+    # but that's harder, as it would require loading all participations
+    Cachex.del(:ics, "event_#{uuid}")
+    clear_instance()
+  end
+
+  # Not applicable for posts
+  @impl Cachable
+  def clear_caches(%Mobilizon.Posts.Post{}) do
+    {:ok, true}
+  end
+
+  @impl Cachable
+  def clear_caches(%Actor{} = actor) do
+    clear_actor_feed(actor)
+
+    clear_instance()
+  end
+
+  defp clear_instance do
+    Cachex.del(:ics, "instance")
+  end
+
+  defp clear_actor_feed(%Actor{preferred_username: preferred_username} = actor) do
+    if Actor.is_public_visibility?(actor) do
+      Cachex.del(:ics, "actor_#{preferred_username}")
+    end
+  end
 end