diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9389df4bd..95f318584 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - OGP rich media parser merged with TwitterCard
 - Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
 - Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated.
+- **Breaking:** Configuration: `:instance, welcome_user_nickname` moved to `:welcome, :direct_message, :sender_nickname`, `:instance, :welcome_message` moved to `:welcome, :direct_message, :message`. Old config namespace is deprecated.
 
 <details>
   <summary>API Changes</summary>
@@ -66,6 +67,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Support pagination in emoji packs API (for packs and for files in pack)
 - Support for viewing instances favicons next to posts and accounts
 - Added Pleroma.Upload.Filter.Exiftool as an alternate EXIF stripping mechanism targeting GPS/location metadata.
+- Configuration: Added `:welcome` settings for the welcome message to newly registered users.
 
 <details>
   <summary>API Changes</summary>
diff --git a/config/config.exs b/config/config.exs
index 406bf2a9b..acf3b5c96 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -225,8 +225,6 @@
   autofollowed_nicknames: [],
   max_pinned_statuses: 1,
   attachment_links: false,
-  welcome_user_nickname: nil,
-  welcome_message: nil,
   max_report_comment_size: 1000,
   safe_dm_mentions: false,
   healthcheck: false,
@@ -254,6 +252,20 @@
     ]
   ]
 
+config :pleroma, :welcome,
+  direct_message: [
+    enabled: false,
+    sender_nickname: nil,
+    message: nil
+  ],
+  email: [
+    enabled: false,
+    sender: nil,
+    subject: "Welcome to <%= instance_name %>",
+    html: "Welcome to <%= instance_name %>",
+    text: "Welcome to <%= instance_name %>"
+  ]
+
 config :pleroma, :feed,
   post_title: %{
     max_length: 100,
diff --git a/config/description.exs b/config/description.exs
index e4850218e..c303fc878 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -778,23 +778,6 @@
         type: :boolean,
         description: "Enable to automatically add attachment link text to statuses"
       },
-      %{
-        key: :welcome_message,
-        type: :string,
-        description:
-          "A message that will be sent to a newly registered users as a direct message",
-        suggestions: [
-          "Hi, @username! Welcome on board!"
-        ]
-      },
-      %{
-        key: :welcome_user_nickname,
-        type: :string,
-        description: "The nickname of the local user that sends the welcome message",
-        suggestions: [
-          "lain"
-        ]
-      },
       %{
         key: :max_report_comment_size,
         type: :integer,
@@ -962,6 +945,84 @@
       }
     ]
   },
+  %{
+    group: :welcome,
+    type: :group,
+    description: "Welcome messages settings",
+    children: [
+      %{
+        group: :direct_message,
+        type: :group,
+        descpiption: "Direct message settings",
+        children: [
+          %{
+            key: :enabled,
+            type: :boolean,
+            description: "Enables sends direct message for new user after registration"
+          },
+          %{
+            key: :message,
+            type: :string,
+            description:
+              "A message that will be sent to a newly registered users as a direct message",
+            suggestions: [
+              "Hi, @username! Welcome on board!"
+            ]
+          },
+          %{
+            key: :sender_nickname,
+            type: :string,
+            description: "The nickname of the local user that sends the welcome message",
+            suggestions: [
+              "lain"
+            ]
+          }
+        ]
+      },
+      %{
+        group: :email,
+        type: :group,
+        descpiption: "Email message settings",
+        children: [
+          %{
+            key: :enabled,
+            type: :boolean,
+            description: "Enables sends direct message for new user after registration"
+          },
+          %{
+            key: :sender,
+            type: [:string, :tuple],
+            description:
+              "The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.",
+            suggestions: [
+              {"Pleroma App", "welcome@pleroma.app"}
+            ]
+          },
+          %{
+            key: :subject,
+            type: :string,
+            description:
+              "The subject of welcome email. Can be use EEX template with `user` and `instance_name` variables.",
+            suggestions: ["Welcome to <%= instance_name%>"]
+          },
+          %{
+            key: :html,
+            type: :string,
+            description:
+              "The html content of welcome email. Can be use EEX template with `user` and `instance_name` variables.",
+            suggestions: ["<h1>Hello <%= user.name%>. Welcome to <%= instance_name%></h1>"]
+          },
+          %{
+            key: :text,
+            type: :string,
+            description:
+              "The text content of welcome email. Can be use EEX template with `user` and `instance_name` variables.",
+            suggestions: ["Hello <%= user.name%>. \n Welcome to <%= instance_name%>\n"]
+          }
+        ]
+      }
+    ]
+  },
   %{
     group: :logger,
     type: :group,
@@ -2235,13 +2296,13 @@
     children: [
       %{
         key: :class,
-        type: [:string, false],
+        type: [:string, :boolean],
         description: "Specify the class to be added to the generated link. Disable to clear.",
         suggestions: ["auto-linker", false]
       },
       %{
         key: :rel,
-        type: [:string, false],
+        type: [:string, :boolean],
         description: "Override the rel attribute. Disable to clear.",
         suggestions: ["ugc", "noopener noreferrer", false]
       },
@@ -2252,7 +2313,7 @@
       },
       %{
         key: :truncate,
-        type: [:integer, false],
+        type: [:integer, :boolean],
         description:
           "Set to a number to truncate URLs longer than the number. Truncated URLs will end in `...`",
         suggestions: [15, false]
diff --git a/config/test.exs b/config/test.exs
index abcf793e5..db0655e73 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -118,6 +118,8 @@
   streaming_enabled: true,
   public_endpoint: nil
 
+config :tzdata, :autoupdate, :disabled
+
 if File.exists?("./config/test.secret.exs") do
   import_config "test.secret.exs"
 else
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index 042ad30c9..5e50f1ba9 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -46,8 +46,6 @@ To add configuration to your config file, you can copy it from the base config.
 * `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
 * `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
 * `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
-* `welcome_message`: A message that will be send to a newly registered users as a direct message.
-* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
 * `max_report_comment_size`: The maximum size of the report comment (Default: `1000`).
 * `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
 * `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``.
@@ -63,6 +61,36 @@ To add configuration to your config file, you can copy it from the base config.
 * `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
 * `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
 
+## Welcome
+* `direct_message`: - welcome message sent as a direct message.
+  * `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`.
+  * `sender_nickname`: The nickname of the local user that sends the welcome message.
+  * `message`: A message that will be send to a newly registered users as a direct message.
+* `email`: - welcome message sent as a email.
+  * `enabled`: Enables the send a welcome email to a newly registered user. Defaults to `false`.
+  * `sender`: The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.
+  * `subject`: A subject of welcome email.
+  * `html`: A html that will be send to a newly registered users as a email.
+  * `text`: A text that will be send to a newly registered users as a email.
+
+    Example:
+
+  ```elixir
+  config :pleroma, :welcome,
+      direct_message: [
+        enabled: true,
+        sender_nickname: "lain",
+        message: "Hi, @username! Welcome on board!"
+        ],
+      email: [
+        enabled: true,
+        sender: {"Pleroma App", "welcome@pleroma.app"},
+        subject: "Welcome to <%= instance_name %>",
+        html: "Welcome to <%= instance_name %>",
+        text: "Welcome to <%= instance_name %>"
+    ]
+  ```
+
 ## Message rewrite facility
 
 ### :mrf
diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex
index 9f0bf6ecb..074492a46 100644
--- a/lib/mix/pleroma.ex
+++ b/lib/mix/pleroma.ex
@@ -24,8 +24,10 @@ def start_pleroma do
       Application.put_env(:logger, :console, level: :debug)
     end
 
+    adapter = Application.get_env(:tesla, :adapter)
+
     apps =
-      if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Gun do
+      if adapter == Tesla.Adapter.Gun do
         [:gun | @apps]
       else
         [:hackney | @apps]
@@ -33,11 +35,14 @@ def start_pleroma do
 
     Enum.each(apps, &Application.ensure_all_started/1)
 
-    children = [
-      Pleroma.Repo,
-      {Pleroma.Config.TransferTask, false},
-      Pleroma.Web.Endpoint
-    ]
+    children =
+      [
+        Pleroma.Repo,
+        {Pleroma.Config.TransferTask, false},
+        Pleroma.Web.Endpoint,
+        {Oban, Pleroma.Config.get(Oban)}
+      ] ++
+        http_children(adapter)
 
     cachex_children = Enum.map(@cachex_children, &Pleroma.Application.build_cachex(&1, []))
 
@@ -115,4 +120,11 @@ def mix_shell?, do: :erlang.function_exported(Mix, :shell, 0)
   def escape_sh_path(path) do
     ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
   end
+
+  defp http_children(Tesla.Adapter.Gun) do
+    Pleroma.Gun.ConnectionPool.children() ++
+      [{Task, &Pleroma.HTTP.AdapterHelper.Gun.limiter_setup/0}]
+  end
+
+  defp http_children(_), do: []
 end
diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex
index ee88c3346..16f62b6f5 100644
--- a/lib/pleroma/application_requirements.ex
+++ b/lib/pleroma/application_requirements.ex
@@ -18,6 +18,7 @@ def verify! do
     :ok
     |> check_confirmation_accounts!
     |> check_migrations_applied!()
+    |> check_welcome_message_config!()
     |> check_rum!()
     |> handle_result()
   end
@@ -25,6 +26,22 @@ def verify! do
   defp handle_result(:ok), do: :ok
   defp handle_result({:error, message}), do: raise(VerifyError, message: message)
 
+  defp check_welcome_message_config!(:ok) do
+    if Pleroma.Config.get([:welcome, :email, :enabled], false) and
+         not Pleroma.Emails.Mailer.enabled?() do
+      Logger.error("""
+      To send welcome email do you need to enable mail.
+      \nconfig :pleroma, Pleroma.Emails.Mailer, enabled: true
+      """)
+
+      {:error, "The mail disabled."}
+    else
+      :ok
+    end
+  end
+
+  defp check_welcome_message_config!(result), do: result
+
   # Checks account confirmation email
   #
   def check_confirmation_accounts!(:ok) do
diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex
index 026871c4f..0f52eb210 100644
--- a/lib/pleroma/config/deprecation_warnings.ex
+++ b/lib/pleroma/config/deprecation_warnings.ex
@@ -55,6 +55,24 @@ def warn do
     mrf_user_allowlist()
     check_old_mrf_config()
     check_media_proxy_whitelist_config()
+    check_welcome_message_config()
+  end
+
+  def check_welcome_message_config do
+    instance_config = Pleroma.Config.get([:instance])
+
+    use_old_config =
+      Keyword.has_key?(instance_config, :welcome_user_nickname) or
+        Keyword.has_key?(instance_config, :welcome_message)
+
+    if use_old_config do
+      Logger.error("""
+      !!!DEPRECATION WARNING!!!
+      Your config is using the old namespace for Welcome messages configuration. You need to change to the new namespace:
+      \n* `config :pleroma, :instance, welcome_user_nickname` is now `config :pleroma, :welcome, :direct_message, :sender_nickname`
+      \n* `config :pleroma, :instance, welcome_message` is now `config :pleroma, :welcome, :direct_message, :message`
+      """)
+    end
   end
 
   def check_old_mrf_config do
diff --git a/lib/pleroma/config/helpers.ex b/lib/pleroma/config/helpers.ex
new file mode 100644
index 000000000..3dce40ea0
--- /dev/null
+++ b/lib/pleroma/config/helpers.ex
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Config.Helpers do
+  alias Pleroma.Config
+
+  def instance_name, do: Config.get([:instance, :name])
+
+  defp instance_notify_email do
+    Config.get([:instance, :notify_email]) || Config.get([:instance, :email])
+  end
+
+  def sender do
+    {instance_name(), instance_notify_email()}
+  end
+end
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index dfadc10b3..313533859 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -12,17 +12,22 @@ defmodule Pleroma.Emails.UserEmail do
   alias Pleroma.Web.Endpoint
   alias Pleroma.Web.Router
 
-  defp instance_name, do: Config.get([:instance, :name])
-
-  defp sender do
-    email = Config.get([:instance, :notify_email]) || Config.get([:instance, :email])
-    {instance_name(), email}
-  end
+  import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
 
   defp recipient(email, nil), do: email
   defp recipient(email, name), do: {name, email}
   defp recipient(%User{} = user), do: recipient(user.email, user.name)
 
+  @spec welcome(User.t(), map()) :: Swoosh.Email.t()
+  def welcome(user, opts \\ %{}) do
+    new()
+    |> to(recipient(user))
+    |> from(Map.get(opts, :sender, sender()))
+    |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!"))
+    |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!"))
+    |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!"))
+  end
+
   def password_reset_email(user, token) when is_binary(token) do
     password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
 
diff --git a/lib/pleroma/reverse_proxy/client/tesla.ex b/lib/pleroma/reverse_proxy/client/tesla.ex
index 65785445d..d5a339681 100644
--- a/lib/pleroma/reverse_proxy/client/tesla.ex
+++ b/lib/pleroma/reverse_proxy/client/tesla.ex
@@ -5,6 +5,8 @@
 defmodule Pleroma.ReverseProxy.Client.Tesla do
   @behaviour Pleroma.ReverseProxy.Client
 
+  alias Pleroma.Gun.ConnectionPool
+
   @type headers() :: [{String.t(), String.t()}]
   @type status() :: pos_integer()
 
@@ -31,6 +33,8 @@ def request(method, url, headers, body, opts \\ []) do
       if is_map(response.body) and method != :head do
         {:ok, response.status, response.headers, response.body}
       else
+        conn_pid = response.opts[:adapter][:conn]
+        ConnectionPool.release_conn(conn_pid)
         {:ok, response.status, response.headers}
       end
     else
@@ -41,15 +45,8 @@ def request(method, url, headers, body, opts \\ []) do
   @impl true
   @spec stream_body(map()) ::
           {:ok, binary(), map()} | {:error, atom() | String.t()} | :done | no_return()
-  def stream_body(%{pid: pid, opts: opts, fin: true}) do
-    # if connection was reused, but in tesla were redirects,
-    # tesla returns new opened connection, which must be closed manually
-    if opts[:old_conn], do: Tesla.Adapter.Gun.close(pid)
-    # if there were redirects we need to checkout old conn
-    conn = opts[:old_conn] || opts[:conn]
-
-    if conn, do: :ok = Pleroma.Gun.ConnectionPool.release_conn(conn)
-
+  def stream_body(%{pid: pid, fin: true}) do
+    ConnectionPool.release_conn(pid)
     :done
   end
 
@@ -74,8 +71,7 @@ defp read_chunk!(%{pid: pid, stream: stream, opts: opts}) do
   @impl true
   @spec close(map) :: :ok | no_return()
   def close(%{pid: pid}) do
-    adapter = check_adapter()
-    adapter.close(pid)
+    ConnectionPool.release_conn(pid)
   end
 
   defp check_adapter do
diff --git a/lib/pleroma/reverse_proxy/reverse_proxy.ex b/lib/pleroma/reverse_proxy/reverse_proxy.ex
index 28ad4c846..0de4e2309 100644
--- a/lib/pleroma/reverse_proxy/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy/reverse_proxy.ex
@@ -165,6 +165,9 @@ defp request(method, url, headers, opts) do
       {:ok, code, _, _} ->
         {:error, {:invalid_http_response, code}}
 
+      {:ok, code, _} ->
+        {:error, {:invalid_http_response, code}}
+
       {:error, error} ->
         {:error, error}
     end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 714ec9a4b..ed1db04c9 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -713,12 +713,33 @@ def register(%Ecto.Changeset{} = changeset) do
   def post_register_action(%User{} = user) do
     with {:ok, user} <- autofollow_users(user),
          {:ok, user} <- set_cache(user),
-         {:ok, _} <- User.WelcomeMessage.post_welcome_message_to_user(user),
+         {:ok, _} <- send_welcome_email(user),
+         {:ok, _} <- send_welcome_message(user),
          {:ok, _} <- try_send_confirmation_email(user) do
       {:ok, user}
     end
   end
 
+  def send_welcome_message(user) do
+    if User.WelcomeMessage.enabled?() do
+      User.WelcomeMessage.post_message(user)
+      {:ok, :enqueued}
+    else
+      {:ok, :noop}
+    end
+  end
+
+  def send_welcome_email(%User{email: email} = user) when is_binary(email) do
+    if User.WelcomeEmail.enabled?() do
+      User.WelcomeEmail.send_email(user)
+      {:ok, :enqueued}
+    else
+      {:ok, :noop}
+    end
+  end
+
+  def send_welcome_email(_), do: {:ok, :noop}
+
   @spec try_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop}
   def try_send_confirmation_email(%User{confirmation_pending: true} = user) do
     if Config.get([:instance, :account_activation_required]) do
diff --git a/lib/pleroma/user/welcome_email.ex b/lib/pleroma/user/welcome_email.ex
new file mode 100644
index 000000000..5322000d4
--- /dev/null
+++ b/lib/pleroma/user/welcome_email.ex
@@ -0,0 +1,62 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.WelcomeEmail do
+  @moduledoc """
+  The module represents the functions to send welcome email.
+  """
+
+  alias Pleroma.Config
+  alias Pleroma.Emails
+  alias Pleroma.User
+
+  import Pleroma.Config.Helpers, only: [instance_name: 0]
+
+  @spec enabled?() :: boolean()
+  def enabled?, do: Config.get([:welcome, :email, :enabled], false)
+
+  @spec send_email(User.t()) :: {:ok, Oban.Job.t()}
+  def send_email(%User{} = user) do
+    user
+    |> Emails.UserEmail.welcome(email_options(user))
+    |> Emails.Mailer.deliver_async()
+  end
+
+  defp email_options(user) do
+    bindings = [user: user, instance_name: instance_name()]
+
+    %{}
+    |> add_sender(Config.get([:welcome, :email, :sender], nil))
+    |> add_option(:subject, bindings)
+    |> add_option(:html, bindings)
+    |> add_option(:text, bindings)
+  end
+
+  defp add_option(opts, option, bindings) do
+    [:welcome, :email, option]
+    |> Config.get(nil)
+    |> eval_string(bindings)
+    |> merge_options(opts, option)
+  end
+
+  defp add_sender(opts, {_name, _email} = sender) do
+    merge_options(sender, opts, :sender)
+  end
+
+  defp add_sender(opts, sender) when is_binary(sender) do
+    add_sender(opts, {instance_name(), sender})
+  end
+
+  defp add_sender(opts, _), do: opts
+
+  defp merge_options(nil, options, _option), do: options
+
+  defp merge_options(value, options, option) do
+    Map.merge(options, %{option => value})
+  end
+
+  defp eval_string(nil, _), do: nil
+  defp eval_string("", _), do: nil
+  defp eval_string(str, bindings), do: EEx.eval_string(str, bindings)
+end
diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex
index f8f520285..86e1c0678 100644
--- a/lib/pleroma/user/welcome_message.ex
+++ b/lib/pleroma/user/welcome_message.ex
@@ -3,32 +3,45 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.User.WelcomeMessage do
+  alias Pleroma.Config
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
 
-  def post_welcome_message_to_user(user) do
-    with %User{} = sender_user <- welcome_user(),
-         message when is_binary(message) <- welcome_message() do
-      CommonAPI.post(sender_user, %{
-        visibility: "direct",
-        status: "@#{user.nickname}\n#{message}"
-      })
-    else
-      _ -> {:ok, nil}
-    end
+  @spec enabled?() :: boolean()
+  def enabled?, do: Config.get([:welcome, :direct_message, :enabled], false)
+
+  @spec post_message(User.t()) :: {:ok, Pleroma.Activity.t() | nil}
+  def post_message(user) do
+    [:welcome, :direct_message, :sender_nickname]
+    |> Config.get(nil)
+    |> fetch_sender()
+    |> do_post(user, welcome_message())
   end
 
-  defp welcome_user do
-    with nickname when is_binary(nickname) <-
-           Pleroma.Config.get([:instance, :welcome_user_nickname]),
-         %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
+  defp do_post(%User{} = sender, %User{nickname: nickname}, message)
+       when is_binary(message) do
+    CommonAPI.post(
+      sender,
+      %{
+        visibility: "direct",
+        status: "@#{nickname}\n#{message}"
+      }
+    )
+  end
+
+  defp do_post(_sender, _recipient, _message), do: {:ok, nil}
+
+  defp fetch_sender(nickname) when is_binary(nickname) do
+    with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
       user
     else
       _ -> nil
     end
   end
 
+  defp fetch_sender(_), do: nil
+
   defp welcome_message do
-    Pleroma.Config.get([:instance, :welcome_message])
+    Config.get([:welcome, :direct_message, :message], nil)
   end
 end
diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex
index cf299bfc2..b1a0d26ab 100644
--- a/lib/pleroma/web/api_spec/operations/chat_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex
@@ -300,11 +300,11 @@ def chat_messages_response do
           "content" => "Check this out :firefox:",
           "id" => "13",
           "chat_id" => "1",
-          "actor_id" => "someflakeid",
+          "account_id" => "someflakeid",
           "unread" => false
         },
         %{
-          "actor_id" => "someflakeid",
+          "account_id" => "someflakeid",
           "content" => "Whats' up?",
           "id" => "12",
           "chat_id" => "1",
diff --git a/priv/repo/migrations/20200724133313_move_welcome_settings.exs b/priv/repo/migrations/20200724133313_move_welcome_settings.exs
new file mode 100644
index 000000000..323a8fcee
--- /dev/null
+++ b/priv/repo/migrations/20200724133313_move_welcome_settings.exs
@@ -0,0 +1,94 @@
+defmodule Pleroma.Repo.Migrations.MoveWelcomeSettings do
+  use Ecto.Migration
+
+  alias Pleroma.ConfigDB
+
+  @old_keys [:welcome_user_nickname, :welcome_message]
+
+  def up do
+    with {:ok, config, {keep_values, move_values}} <- get_old_values() do
+      insert_welcome_settings(move_values)
+      update_instance_config(config, keep_values)
+    end
+  end
+
+  def down do
+    with {:ok, welcome_config, revert_values} <- get_revert_values() do
+      revert_instance_config(revert_values)
+      Pleroma.Repo.delete(welcome_config)
+    end
+  end
+
+  defp insert_welcome_settings([_ | _] = values) do
+    unless String.trim(values[:welcome_message]) == "" do
+      config_values = [
+        direct_message: %{
+          enabled: true,
+          sender_nickname: values[:welcome_user_nickname],
+          message: values[:welcome_message]
+        },
+        email: %{
+          enabled: false,
+          sender: nil,
+          subject: "Welcome to <%= instance_name %>",
+          html: "Welcome to <%= instance_name %>",
+          text: "Welcome to <%= instance_name %>"
+        }
+      ]
+
+      {:ok, _} =
+        %ConfigDB{}
+        |> ConfigDB.changeset(%{group: :pleroma, key: :welcome, value: config_values})
+        |> Pleroma.Repo.insert()
+    end
+
+    :ok
+  end
+
+  defp insert_welcome_settings(_), do: :noop
+
+  defp revert_instance_config(%{} = revert_values) do
+    values = [
+      welcome_user_nickname: revert_values[:sender_nickname],
+      welcome_message: revert_values[:message]
+    ]
+
+    ConfigDB.update_or_create(%{group: :pleroma, key: :instance, value: values})
+  end
+
+  defp revert_instance_config(_), do: :noop
+
+  defp update_instance_config(config, values) do
+    {:ok, _} =
+      config
+      |> ConfigDB.changeset(%{value: values})
+      |> Pleroma.Repo.update()
+
+    :ok
+  end
+
+  defp get_revert_values do
+    config = ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+
+    cond do
+      is_nil(config) -> {:noop, nil, nil}
+      true -> {:ok, config, config.value[:direct_message]}
+    end
+  end
+
+  defp get_old_values do
+    config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+    cond do
+      is_nil(config) ->
+        {:noop, config, {}}
+
+      is_binary(config.value[:welcome_message]) ->
+        {:ok, config,
+         {Keyword.drop(config.value, @old_keys), Keyword.take(config.value, @old_keys)}}
+
+      true ->
+        {:ok, config, {Keyword.drop(config.value, @old_keys), []}}
+    end
+  end
+end
diff --git a/test/application_requirements_test.exs b/test/application_requirements_test.exs
index fc609d174..21d24ddd0 100644
--- a/test/application_requirements_test.exs
+++ b/test/application_requirements_test.exs
@@ -9,6 +9,20 @@ defmodule Pleroma.ApplicationRequirementsTest do
 
   alias Pleroma.Repo
 
+  describe "check_welcome_message_config!/1" do
+    setup do: clear_config([:welcome])
+    setup do: clear_config([Pleroma.Emails.Mailer])
+
+    test "raises if welcome email enabled but mail disabled" do
+      Pleroma.Config.put([:welcome, :email, :enabled], true)
+      Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
+
+      assert_raise Pleroma.ApplicationRequirements.VerifyError, "The mail disabled.", fn ->
+        capture_log(&Pleroma.ApplicationRequirements.verify!/0)
+      end
+    end
+  end
+
   describe "check_confirmation_accounts!" do
     setup_with_mocks([
       {Pleroma.ApplicationRequirements, [:passthrough],
diff --git a/test/migrations/20200724133313_move_welcome_settings_test.exs b/test/migrations/20200724133313_move_welcome_settings_test.exs
new file mode 100644
index 000000000..739f24547
--- /dev/null
+++ b/test/migrations/20200724133313_move_welcome_settings_test.exs
@@ -0,0 +1,140 @@
+defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  import Pleroma.Tests.Helpers
+  alias Pleroma.ConfigDB
+
+  setup_all do: require_migration("20200724133313_move_welcome_settings")
+
+  describe "up/0" do
+    test "converts welcome settings", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :instance,
+        value: [
+          welcome_message: "Test message",
+          welcome_user_nickname: "jimm",
+          name: "Pleroma"
+        ]
+      )
+
+      migration.up()
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      welcome_config = ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+
+      assert instance_config.value == [name: "Pleroma"]
+
+      assert welcome_config.value == [
+               direct_message: %{
+                 enabled: true,
+                 message: "Test message",
+                 sender_nickname: "jimm"
+               },
+               email: %{
+                 enabled: false,
+                 html: "Welcome to <%= instance_name %>",
+                 sender: nil,
+                 subject: "Welcome to <%= instance_name %>",
+                 text: "Welcome to <%= instance_name %>"
+               }
+             ]
+    end
+
+    test "does nothing when message empty", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :instance,
+        value: [
+          welcome_message: "",
+          welcome_user_nickname: "jimm",
+          name: "Pleroma"
+        ]
+      )
+
+      migration.up()
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      assert instance_config.value == [name: "Pleroma"]
+    end
+
+    test "does nothing when welcome_message not set", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :instance,
+        value: [welcome_user_nickname: "jimm", name: "Pleroma"]
+      )
+
+      migration.up()
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      assert instance_config.value == [name: "Pleroma"]
+    end
+  end
+
+  describe "down/0" do
+    test "revert new settings to old when instance setting not exists", %{migration: migration} do
+      insert(:config,
+        group: :pleroma,
+        key: :welcome,
+        value: [
+          direct_message: %{
+            enabled: true,
+            message: "Test message",
+            sender_nickname: "jimm"
+          },
+          email: %{
+            enabled: false,
+            html: "Welcome to <%= instance_name %>",
+            sender: nil,
+            subject: "Welcome to <%= instance_name %>",
+            text: "Welcome to <%= instance_name %>"
+          }
+        ]
+      )
+
+      migration.down()
+
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+      assert instance_config.value == [
+               welcome_user_nickname: "jimm",
+               welcome_message: "Test message"
+             ]
+    end
+
+    test "revert new settings to old when instance setting exists", %{migration: migration} do
+      insert(:config, group: :pleroma, key: :instance, value: [name: "Pleroma App"])
+
+      insert(:config,
+        group: :pleroma,
+        key: :welcome,
+        value: [
+          direct_message: %{
+            enabled: true,
+            message: "Test message",
+            sender_nickname: "jimm"
+          },
+          email: %{
+            enabled: false,
+            html: "Welcome to <%= instance_name %>",
+            sender: nil,
+            subject: "Welcome to <%= instance_name %>",
+            text: "Welcome to <%= instance_name %>"
+          }
+        ]
+      )
+
+      migration.down()
+
+      refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome})
+      instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
+
+      assert instance_config.value == [
+               name: "Pleroma App",
+               welcome_user_nickname: "jimm",
+               welcome_message: "Test message"
+             ]
+    end
+  end
+end
diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs
index 71f36c0e3..fb12e7fb3 100644
--- a/test/tasks/config_test.exs
+++ b/test/tasks/config_test.exs
@@ -129,8 +129,6 @@ test "load a settings with large values and pass to file", %{temp_file: temp_fil
           autofollowed_nicknames: [],
           max_pinned_statuses: 1,
           attachment_links: false,
-          welcome_user_nickname: nil,
-          welcome_message: nil,
           max_report_comment_size: 1000,
           safe_dm_mentions: false,
           healthcheck: false,
@@ -172,7 +170,7 @@ test "load a settings with large values and pass to file", %{temp_file: temp_fil
         end
 
       assert file ==
-               "#{header}\n\nconfig :pleroma, :instance,\n  name: \"Pleroma\",\n  email: \"example@example.com\",\n  notify_email: \"noreply@example.com\",\n  description: \"A Pleroma instance, an alternative fediverse server\",\n  limit: 5000,\n  chat_limit: 5000,\n  remote_limit: 100_000,\n  upload_limit: 16_000_000,\n  avatar_upload_limit: 2_000_000,\n  background_upload_limit: 4_000_000,\n  banner_upload_limit: 4_000_000,\n  poll_limits: %{\n    max_expiration: 31_536_000,\n    max_option_chars: 200,\n    max_options: 20,\n    min_expiration: 0\n  },\n  registrations_open: true,\n  federating: true,\n  federation_incoming_replies_max_depth: 100,\n  federation_reachability_timeout_days: 7,\n  federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n  allow_relay: true,\n  public: true,\n  quarantined_instances: [],\n  managed_config: true,\n  static_dir: \"instance/static/\",\n  allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n  autofollowed_nicknames: [],\n  max_pinned_statuses: 1,\n  attachment_links: false,\n  welcome_user_nickname: nil,\n  welcome_message: nil,\n  max_report_comment_size: 1000,\n  safe_dm_mentions: false,\n  healthcheck: false,\n  remote_post_retention_days: 90,\n  skip_thread_containment: true,\n  limit_to_local_content: :unauthenticated,\n  user_bio_length: 5000,\n  user_name_length: 100,\n  max_account_fields: 10,\n  max_remote_account_fields: 20,\n  account_field_name_length: 512,\n  account_field_value_length: 2048,\n  external_user_synchronization: true,\n  extended_nickname_format: true,\n  multi_factor_authentication: [\n    totp: [digits: 6, period: 30],\n    backup_codes: [number: 2, length: 6]\n  ]\n"
+               "#{header}\n\nconfig :pleroma, :instance,\n  name: \"Pleroma\",\n  email: \"example@example.com\",\n  notify_email: \"noreply@example.com\",\n  description: \"A Pleroma instance, an alternative fediverse server\",\n  limit: 5000,\n  chat_limit: 5000,\n  remote_limit: 100_000,\n  upload_limit: 16_000_000,\n  avatar_upload_limit: 2_000_000,\n  background_upload_limit: 4_000_000,\n  banner_upload_limit: 4_000_000,\n  poll_limits: %{\n    max_expiration: 31_536_000,\n    max_option_chars: 200,\n    max_options: 20,\n    min_expiration: 0\n  },\n  registrations_open: true,\n  federating: true,\n  federation_incoming_replies_max_depth: 100,\n  federation_reachability_timeout_days: 7,\n  federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n  allow_relay: true,\n  public: true,\n  quarantined_instances: [],\n  managed_config: true,\n  static_dir: \"instance/static/\",\n  allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n  autofollowed_nicknames: [],\n  max_pinned_statuses: 1,\n  attachment_links: false,\n  max_report_comment_size: 1000,\n  safe_dm_mentions: false,\n  healthcheck: false,\n  remote_post_retention_days: 90,\n  skip_thread_containment: true,\n  limit_to_local_content: :unauthenticated,\n  user_bio_length: 5000,\n  user_name_length: 100,\n  max_account_fields: 10,\n  max_remote_account_fields: 20,\n  account_field_name_length: 512,\n  account_field_value_length: 2048,\n  external_user_synchronization: true,\n  extended_nickname_format: true,\n  multi_factor_authentication: [\n    totp: [digits: 6, period: 30],\n    backup_codes: [number: 2, length: 6]\n  ]\n"
     end
   end
 end
diff --git a/test/user/welcome_email_test.exs b/test/user/welcome_email_test.exs
new file mode 100644
index 000000000..d005d11b2
--- /dev/null
+++ b/test/user/welcome_email_test.exs
@@ -0,0 +1,61 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.WelcomeEmailTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Config
+  alias Pleroma.Tests.ObanHelpers
+  alias Pleroma.User.WelcomeEmail
+
+  import Pleroma.Factory
+  import Swoosh.TestAssertions
+
+  setup do: clear_config([:welcome])
+
+  describe "send_email/1" do
+    test "send a welcome email" do
+      user = insert(:user, name: "Jimm")
+
+      Config.put([:welcome, :email, :enabled], true)
+      Config.put([:welcome, :email, :sender], "welcome@pleroma.app")
+
+      Config.put(
+        [:welcome, :email, :subject],
+        "Hello, welcome to pleroma: <%= instance_name %>"
+      )
+
+      Config.put(
+        [:welcome, :email, :html],
+        "<h1>Hello <%= user.name %>.</h1> <p>Welcome to <%= instance_name %></p>"
+      )
+
+      instance_name = Config.get([:instance, :name])
+
+      {:ok, _job} = WelcomeEmail.send_email(user)
+
+      ObanHelpers.perform_all()
+
+      assert_email_sent(
+        from: {instance_name, "welcome@pleroma.app"},
+        to: {user.name, user.email},
+        subject: "Hello, welcome to pleroma: #{instance_name}",
+        html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>"
+      )
+
+      Config.put([:welcome, :email, :sender], {"Pleroma App", "welcome@pleroma.app"})
+
+      {:ok, _job} = WelcomeEmail.send_email(user)
+
+      ObanHelpers.perform_all()
+
+      assert_email_sent(
+        from: {"Pleroma App", "welcome@pleroma.app"},
+        to: {user.name, user.email},
+        subject: "Hello, welcome to pleroma: #{instance_name}",
+        html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>"
+      )
+    end
+  end
+end
diff --git a/test/user/welcome_message_test.exs b/test/user/welcome_message_test.exs
new file mode 100644
index 000000000..3cd6f5cb7
--- /dev/null
+++ b/test/user/welcome_message_test.exs
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.WelcomeMessageTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Config
+  alias Pleroma.User.WelcomeMessage
+
+  import Pleroma.Factory
+
+  setup do: clear_config([:welcome])
+
+  describe "post_message/1" do
+    test "send a direct welcome message" do
+      welcome_user = insert(:user)
+      user = insert(:user, name: "Jimm")
+
+      Config.put([:welcome, :direct_message, :enabled], true)
+      Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
+
+      Config.put(
+        [:welcome, :direct_message, :message],
+        "Hello. Welcome to Pleroma"
+      )
+
+      {:ok, %Pleroma.Activity{} = activity} = WelcomeMessage.post_message(user)
+      assert user.ap_id in activity.recipients
+      assert activity.data["directMessage"] == true
+      assert Pleroma.Object.normalize(activity).data["content"] =~ "Hello. Welcome to Pleroma"
+    end
+  end
+end
diff --git a/test/user_test.exs b/test/user_test.exs
index 21c03b470..d087e9101 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -388,8 +388,7 @@ test "fetches correct profile for nickname beginning with number" do
     }
 
     setup do: clear_config([:instance, :autofollowed_nicknames])
-    setup do: clear_config([:instance, :welcome_message])
-    setup do: clear_config([:instance, :welcome_user_nickname])
+    setup do: clear_config([:welcome])
     setup do: clear_config([:instance, :account_activation_required])
 
     test "it autofollows accounts that are set for it" do
@@ -411,17 +410,35 @@ test "it autofollows accounts that are set for it" do
 
     test "it sends a welcome message if it is set" do
       welcome_user = insert(:user)
+      Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
+      Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
+      Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a cool site")
 
-      Pleroma.Config.put([:instance, :welcome_user_nickname], welcome_user.nickname)
-      Pleroma.Config.put([:instance, :welcome_message], "Hello, this is a cool site")
+      Pleroma.Config.put([:welcome, :email, :enabled], true)
+      Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email)
+
+      Pleroma.Config.put(
+        [:welcome, :email, :subject],
+        "Hello, welcome to cool site: <%= instance_name %>"
+      )
+
+      instance_name = Pleroma.Config.get([:instance, :name])
 
       cng = User.register_changeset(%User{}, @full_user_data)
       {:ok, registered_user} = User.register(cng)
+      ObanHelpers.perform_all()
 
       activity = Repo.one(Pleroma.Activity)
       assert registered_user.ap_id in activity.recipients
       assert Object.normalize(activity).data["content"] =~ "cool site"
       assert activity.actor == welcome_user.ap_id
+
+      assert_email_sent(
+        from: {instance_name, welcome_user.email},
+        to: {registered_user.name, registered_user.email},
+        subject: "Hello, welcome to cool site: #{instance_name}",
+        html_body: "Welcome to #{instance_name}"
+      )
     end
 
     test "it sends a confirm email" do