diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index f58db9ef91..73d84bc4df 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -374,3 +374,8 @@ block.settings.field_block:*:*:
   mapping:
     formatter:
       type: field_formatter
+
+# Schema for entity actions.
+action.configuration.entity:*:*:
+  type: action_configuration_default
+  label: 'Entity action'
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php
new file mode 100644
index 0000000000..29159c2931
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityActionDeriverBase.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a base action for each entity type with specific interfaces.
+ */
+abstract class EntityActionDeriverBase extends DeriverBase implements ContainerDeriverInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a new EntityActionDeriverBase object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static($container->get('entity_type.manager'));
+  }
+
+  /**
+   * Indicates whether the deriver can be used for the provided entity type.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return bool
+   *   TRUE if the entity type can be used, FALSE otherwise.
+   */
+  abstract protected function isApplicable(EntityTypeInterface $entity_type);
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    if (empty($this->derivatives)) {
+      $definitions = [];
+      foreach ($this->getApplicableEntityTypes() as $entity_type_id => $entity_type) {
+        $definition = $base_plugin_definition;
+        $definition['type'] = $entity_type_id;
+        $definition['label'] = sprintf('%s %s', $base_plugin_definition['action_label'], $entity_type->getSingularLabel());
+        $definitions[$entity_type_id] = $definition;
+      }
+      $this->derivatives = $definitions;
+    }
+
+    return parent::getDerivativeDefinitions($base_plugin_definition);
+  }
+
+  /**
+   * Gets a list of applicable entity types.
+   *
+   * The list consists of all entity types which match the conditions for the
+   * given deriver.
+   * For example, if the action applies to entities that are publishable,
+   * this method will find all entity types that are publishable.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface[]
+   *   The applicable entity types, keyed by entity type ID.
+   */
+  protected function getApplicableEntityTypes() {
+    $entity_types = $this->entityTypeManager->getDefinitions();
+    $entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
+      return $this->isApplicable($entity_type);
+    });
+
+    return $entity_types;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityChangedActionDeriver.php b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityChangedActionDeriver.php
new file mode 100644
index 0000000000..2df090b0d9
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityChangedActionDeriver.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action\Derivative;
+
+use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Provides an action deriver that finds entity types of EntityChangedInterface.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\SaveAction
+ */
+class EntityChangedActionDeriver extends EntityActionDeriverBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function isApplicable(EntityTypeInterface $entity_type) {
+    return $entity_type->entityClassImplements(EntityChangedInterface::class);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityPublishedActionDeriver.php b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityPublishedActionDeriver.php
new file mode 100644
index 0000000000..05548eeba8
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/Derivative/EntityPublishedActionDeriver.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action\Derivative;
+
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Provides an action deriver that finds publishable entity types.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\PublishAction
+ * @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
+ */
+class EntityPublishedActionDeriver extends EntityActionDeriverBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function isApplicable(EntityTypeInterface $entity_type) {
+    return $entity_type->entityClassImplements(EntityPublishedInterface::class);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/EntityActionBase.php b/core/lib/Drupal/Core/Action/Plugin/Action/EntityActionBase.php
new file mode 100644
index 0000000000..2624024903
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/EntityActionBase.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action;
+
+use Drupal\Component\Plugin\DependentPluginInterface;
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Base class for entity-based actions.
+ */
+abstract class EntityActionBase extends ActionBase implements DependentPluginInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs a EntityActionBase object.
+   *
+   * @param mixed[] $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    $module_name = $this->entityTypeManager
+      ->getDefinition($this->getPluginDefinition()['type'])
+      ->getProvider();
+    return ['module' => [$module_name]];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/PublishAction.php b/core/lib/Drupal/Core/Action/Plugin/Action/PublishAction.php
new file mode 100644
index 0000000000..8d0cf756b4
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/PublishAction.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Publishes an entity.
+ *
+ * @Action(
+ *   id = "entity:publish_action",
+ *   action_label = @Translation("Publish"),
+ *   deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver",
+ * )
+ */
+class PublishAction extends EntityActionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($entity = NULL) {
+    $entity->setPublished()->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    $key = $object->getEntityType()->getKey('published');
+
+    /** @var \Drupal\Core\Entity\EntityInterface $object */
+    $result = $object->access('update', $account, TRUE)
+      ->andIf($object->$key->access('edit', $account, TRUE));
+
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/SaveAction.php b/core/lib/Drupal/Core/Action/Plugin/Action/SaveAction.php
new file mode 100644
index 0000000000..fc63e5c16e
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/SaveAction.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action;
+
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides an action that can save any entity.
+ *
+ * @Action(
+ *   id = "entity:save_action",
+ *   action_label = @Translation("Save"),
+ *   deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver",
+ * )
+ */
+class SaveAction extends EntityActionBase {
+
+  /**
+   * The time service.
+   *
+   * @var \Drupal\Component\Datetime\TimeInterface
+   */
+  protected $time;
+
+  /**
+   * Constructs a SaveAction object.
+   *
+   * @param mixed[] $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Component\Datetime\TimeInterface $time
+   *   The time service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
+    $this->time = $time;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('datetime.time')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($entity = NULL) {
+    $entity->setChangedTime($this->time->getRequestTime())->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    // It's not necessary to check the changed field access here, because
+    // Drupal\Core\Field\ChangedFieldItemList would anyway return 'not allowed'.
+    // Also changing the changed field value is only a workaround to trigger an
+    // entity resave. Without a field change, this would not be possible.
+    /** @var \Drupal\Core\Entity\EntityInterface $object */
+    return $object->access('update', $account, $return_as_object);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Action/Plugin/Action/UnpublishAction.php b/core/lib/Drupal/Core/Action/Plugin/Action/UnpublishAction.php
new file mode 100644
index 0000000000..bb3f6c2d98
--- /dev/null
+++ b/core/lib/Drupal/Core/Action/Plugin/Action/UnpublishAction.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\Core\Action\Plugin\Action;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Unpublishes an entity.
+ *
+ * @Action(
+ *   id = "entity:unpublish_action",
+ *   action_label = @Translation("Unpublish"),
+ *   deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver",
+ * )
+ */
+class UnpublishAction extends EntityActionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($entity = NULL) {
+    $entity->setUnpublished()->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    $key = $object->getEntityType()->getKey('published');
+
+    /** @var \Drupal\Core\Entity\EntityInterface $object */
+    $result = $object->access('update', $account, TRUE)
+      ->andIf($object->$key->access('edit', $account, TRUE));
+
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+}
diff --git a/core/modules/action/migrations/d6_action.yml b/core/modules/action/migrations/d6_action.yml
index 592ed1805d..38b7fb9a2c 100644
--- a/core/modules/action/migrations/d6_action.yml
+++ b/core/modules/action/migrations/d6_action.yml
@@ -23,6 +23,12 @@ process:
         imagecache_flush_action: 0
         imagecache_generate_all_action: 0
         imagecache_generate_action: 0
+        comment_publish_action: entity:publish_action:comment
+        comment_unpublish_action: entity:unpublish_action:comment
+        comment_save_action: entity:save_action:comment
+        node_publish_action: entity:publish_action:node
+        node_unpublish_action: entity:unpublish_action:node
+        node_save_action: entity:save_action:node
       bypass: true
     -
       plugin: skip_on_empty
diff --git a/core/modules/action/migrations/d7_action.yml b/core/modules/action/migrations/d7_action.yml
index 03b869af18..ccafcaf0b3 100644
--- a/core/modules/action/migrations/d7_action.yml
+++ b/core/modules/action/migrations/d7_action.yml
@@ -20,6 +20,12 @@ process:
         system_send_email_action: action_send_email_action
         system_message_action: action_message_action
         system_block_ip_action: 0
+        comment_publish_action: entity:publish_action:comment
+        comment_unpublish_action: entity:unpublish_action:comment
+        comment_save_action: entity:save_action:comment
+        node_publish_action: entity:publish_action:node
+        node_unpublish_action: entity:unpublish_action:node
+        node_save_action: entity:save_action:node
       bypass: true
     -
       plugin: skip_on_empty
diff --git a/core/modules/comment/config/install/system.action.comment_publish_action.yml b/core/modules/comment/config/install/system.action.comment_publish_action.yml
index 8fbd48d349..7a47b2d9e6 100644
--- a/core/modules/comment/config/install/system.action.comment_publish_action.yml
+++ b/core/modules/comment/config/install/system.action.comment_publish_action.yml
@@ -6,5 +6,5 @@ dependencies:
 id: comment_publish_action
 label: 'Publish comment'
 type: comment
-plugin: comment_publish_action
+plugin: entity:publish_action:comment
 configuration: {  }
diff --git a/core/modules/comment/config/install/system.action.comment_save_action.yml b/core/modules/comment/config/install/system.action.comment_save_action.yml
index 640d2811cd..614cf4b681 100644
--- a/core/modules/comment/config/install/system.action.comment_save_action.yml
+++ b/core/modules/comment/config/install/system.action.comment_save_action.yml
@@ -6,5 +6,5 @@ dependencies:
 id: comment_save_action
 label: 'Save comment'
 type: comment
-plugin: comment_save_action
+plugin: entity:save_action:comment
 configuration: {  }
diff --git a/core/modules/comment/config/install/system.action.comment_unpublish_action.yml b/core/modules/comment/config/install/system.action.comment_unpublish_action.yml
index 99902e8be3..a1800b4a8c 100644
--- a/core/modules/comment/config/install/system.action.comment_unpublish_action.yml
+++ b/core/modules/comment/config/install/system.action.comment_unpublish_action.yml
@@ -6,5 +6,5 @@ dependencies:
 id: comment_unpublish_action
 label: 'Unpublish comment'
 type: comment
-plugin: comment_unpublish_action
+plugin: entity:unpublish_action:comment
 configuration: {  }
diff --git a/core/modules/comment/config/schema/comment.schema.yml b/core/modules/comment/config/schema/comment.schema.yml
index e4a5b75d4a..c29088b1a9 100644
--- a/core/modules/comment/config/schema/comment.schema.yml
+++ b/core/modules/comment/config/schema/comment.schema.yml
@@ -15,10 +15,14 @@ field.widget.settings.comment_default:
   type: mapping
   label: 'Comment display format settings'
 
+# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2919303
 action.configuration.comment_publish_action:
   type: action_configuration_default
   label: 'Publish comment configuration'
 
+# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2919303
 action.configuration.comment_save_action:
   type: action_configuration_default
   label: 'Save comment configuration'
@@ -34,6 +38,8 @@ action.configuration.comment_unpublish_by_keyword_action:
         type: string
         label: 'Keyword'
 
+# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2919303
 action.configuration.comment_unpublish_action:
   type: action_configuration_default
   label: 'Unpublish comment configuration'
diff --git a/core/modules/comment/src/Plugin/Action/PublishComment.php b/core/modules/comment/src/Plugin/Action/PublishComment.php
index 14f5605c74..9aa1e4aa64 100644
--- a/core/modules/comment/src/Plugin/Action/PublishComment.php
+++ b/core/modules/comment/src/Plugin/Action/PublishComment.php
@@ -2,37 +2,32 @@
 
 namespace Drupal\comment\Plugin\Action;
 
-use Drupal\Core\Action\ActionBase;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Action\Plugin\Action\PublishAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Publishes a comment.
  *
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\Core\Action\Plugin\Action\PublishAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\PublishAction
+ * @see https://www.drupal.org/node/2919303
+ *
  * @Action(
  *   id = "comment_publish_action",
  *   label = @Translation("Publish comment"),
  *   type = "comment"
  * )
  */
-class PublishComment extends ActionBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function execute($comment = NULL) {
-    $comment->setPublished(TRUE);
-    $comment->save();
-  }
+class PublishComment extends PublishAction {
 
   /**
    * {@inheritdoc}
    */
-  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\comment\CommentInterface $object */
-    $result = $object->status->access('edit', $account, TRUE)
-      ->andIf($object->access('update', $account, TRUE));
-
-    return $return_as_object ? $result : $result->isAllowed();
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
+    @trigger_error(__NAMESPACE__ . '\PublishComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\PublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/comment/src/Plugin/Action/SaveComment.php b/core/modules/comment/src/Plugin/Action/SaveComment.php
index fdab65d234..c197112e4f 100644
--- a/core/modules/comment/src/Plugin/Action/SaveComment.php
+++ b/core/modules/comment/src/Plugin/Action/SaveComment.php
@@ -2,33 +2,33 @@
 
 namespace Drupal\comment\Plugin\Action;
 
-use Drupal\Core\Action\ActionBase;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Action\Plugin\Action\SaveAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Saves a comment.
  *
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\Core\Action\Plugin\Action\SaveAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\SaveAction
+ * @see https://www.drupal.org/node/2919303
+ *
  * @Action(
  *   id = "comment_save_action",
  *   label = @Translation("Save comment"),
  *   type = "comment"
  * )
  */
-class SaveComment extends ActionBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function execute($comment = NULL) {
-    $comment->save();
-  }
+class SaveComment extends SaveAction {
 
   /**
    * {@inheritdoc}
    */
-  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\comment\CommentInterface $object */
-    return $object->access('update', $account, $return_as_object);
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $time);
+    @trigger_error(__NAMESPACE__ . '\SaveComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\SaveAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/comment/src/Plugin/Action/UnpublishComment.php b/core/modules/comment/src/Plugin/Action/UnpublishComment.php
index 6accccc834..7e867c0453 100644
--- a/core/modules/comment/src/Plugin/Action/UnpublishComment.php
+++ b/core/modules/comment/src/Plugin/Action/UnpublishComment.php
@@ -2,37 +2,32 @@
 
 namespace Drupal\comment\Plugin\Action;
 
-use Drupal\Core\Action\ActionBase;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Action\Plugin\Action\UnpublishAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Unpublishes a comment.
  *
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
+ * @see https://www.drupal.org/node/2919303
+ *
  * @Action(
  *   id = "comment_unpublish_action",
  *   label = @Translation("Unpublish comment"),
  *   type = "comment"
  * )
  */
-class UnpublishComment extends ActionBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function execute($comment = NULL) {
-    $comment->setPublished(FALSE);
-    $comment->save();
-  }
+class UnpublishComment extends UnpublishAction {
 
   /**
    * {@inheritdoc}
    */
-  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\comment\CommentInterface $object */
-    $result = $object->status->access('edit', $account, TRUE)
-      ->andIf($object->access('update', $account, TRUE));
-
-    return $return_as_object ? $result : $result->isAllowed();
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
+    @trigger_error(__NAMESPACE__ . '\UnpublishComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/comment/tests/src/Functional/CommentActionsTest.php b/core/modules/comment/tests/src/Functional/CommentActionsTest.php
index 79786fcc05..326749b7c0 100644
--- a/core/modules/comment/tests/src/Functional/CommentActionsTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentActionsTest.php
@@ -32,7 +32,7 @@ public function testCommentPublishUnpublishActions() {
     $action = Action::load('comment_unpublish_action');
     $action->execute([$comment]);
     $this->assertTrue($comment->isPublished() === FALSE, 'Comment was unpublished');
-
+    $this->assertArraySubset(['module' => ['comment']], $action->getDependencies());
     // Publish a comment.
     $action = Action::load('comment_publish_action');
     $action->execute([$comment]);
diff --git a/core/modules/content_moderation/content_moderation.module b/core/modules/content_moderation/content_moderation.module
index 5a7af0e2c9..2ae1e0da3a 100644
--- a/core/modules/content_moderation/content_moderation.module
+++ b/core/modules/content_moderation/content_moderation.module
@@ -8,8 +8,8 @@
 use Drupal\content_moderation\EntityOperations;
 use Drupal\content_moderation\EntityTypeInfo;
 use Drupal\content_moderation\ContentPreprocess;
-use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublishNode;
-use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublishNode;
+use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish;
+use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublish;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
@@ -21,8 +21,8 @@
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\workflows\WorkflowInterface;
-use Drupal\node\Plugin\Action\PublishNode;
-use Drupal\node\Plugin\Action\UnpublishNode;
+use Drupal\Core\Action\Plugin\Action\PublishAction;
+use Drupal\Core\Action\Plugin\Action\UnpublishAction;
 use Drupal\workflows\Entity\Workflow;
 
 /**
@@ -227,13 +227,15 @@ function content_moderation_action_info_alter(&$definitions) {
 
   // The publish/unpublish actions are not valid on moderated entities. So swap
   // their implementations out for alternates that will become a no-op on a
-  // moderated node. If another module has already swapped out those classes,
+  // moderated entity. If another module has already swapped out those classes,
   // though, we'll be polite and do nothing.
-  if (isset($definitions['node_publish_action']['class']) && $definitions['node_publish_action']['class'] == PublishNode::class) {
-    $definitions['node_publish_action']['class'] = ModerationOptOutPublishNode::class;
-  }
-  if (isset($definitions['node_unpublish_action']['class']) && $definitions['node_unpublish_action']['class'] == UnpublishNode::class) {
-    $definitions['node_unpublish_action']['class'] = ModerationOptOutUnpublishNode::class;
+  foreach ($definitions as &$definition) {
+    if ($definition['id'] === 'entity:publish_action' && $definition['class'] == PublishAction::class) {
+      $definition['class'] = ModerationOptOutPublish::class;
+    }
+    if ($definition['id'] === 'entity:unpublish_action' && $definition['class'] == UnpublishAction::class) {
+      $definition['class'] = ModerationOptOutUnpublish::class;
+    }
   }
 }
 
diff --git a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublish.php
similarity index 59%
copy from core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php
copy to core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublish.php
index b2d85335aa..6a707e852f 100644
--- a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php
+++ b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublish.php
@@ -3,18 +3,20 @@
 namespace Drupal\content_moderation\Plugin\Action;
 
 use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Action\Plugin\Action\PublishAction;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\node\Plugin\Action\PublishNode;
 use Drupal\content_moderation\ModerationInformationInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Alternate action plugin that can opt-out of modifying moderated entities.
  *
- * @see \Drupal\node\Plugin\Action\PublishNode
+ * @see \Drupal\Core\Action\Plugin\Action\PublishAction
  */
-class ModerationOptOutPublishNode extends PublishNode implements ContainerFactoryPluginInterface {
+class ModerationOptOutPublish extends PublishAction implements ContainerFactoryPluginInterface {
 
   /**
    * Moderation information service.
@@ -24,7 +26,14 @@ class ModerationOptOutPublishNode extends PublishNode implements ContainerFactor
   protected $moderationInfo;
 
   /**
-   * ModerationOptOutPublishNode constructor.
+   * Bundle info service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $bundleInfo;
+
+  /**
+   * ModerationOptOutPublish constructor.
    *
    * @param array $configuration
    *   A configuration array containing information about the plugin instance.
@@ -32,12 +41,17 @@ class ModerationOptOutPublishNode extends PublishNode implements ContainerFactor
    *   The plugin_id for the plugin instance.
    * @param mixed $plugin_definition
    *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
    * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
    *   The moderation information service.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
+   *   Bundle info service.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModerationInformationInterface $moderation_info) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_info, EntityTypeBundleInfoInterface $bundle_info) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
     $this->moderationInfo = $moderation_info;
+    $this->bundleInfo = $bundle_info;
   }
 
   /**
@@ -46,7 +60,9 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     return new static(
       $configuration, $plugin_id, $plugin_definition,
-      $container->get('content_moderation.moderation_information')
+      $container->get('entity_type.manager'),
+      $container->get('content_moderation.moderation_information'),
+      $container->get('entity_type.bundle.info')
     );
   }
 
@@ -54,9 +70,11 @@ public static function create(ContainerInterface $container, array $configuratio
    * {@inheritdoc}
    */
   public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\node\NodeInterface $entity */
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     if ($entity && $this->moderationInfo->isModeratedEntity($entity)) {
-      drupal_set_message($this->t("@bundle @label were skipped as they are under moderation and may not be directly published.", ['@bundle' => node_get_type_label($entity), '@label' => $entity->getEntityType()->getPluralLabel()]), 'warning');
+      $bundle_info = $this->bundleInfo->getBundleInfo($entity->getEntityTypeId());
+      $bundle_label = $bundle_info[$entity->bundle()]['label'];
+      drupal_set_message($this->t("@bundle @label were skipped as they are under moderation and may not be directly published.", ['@bundle' => $bundle_label, '@label' => $entity->getEntityType()->getPluralLabel()]), 'warning');
       $result = AccessResult::forbidden();
       return $return_as_object ? $result : $result->isAllowed();
     }
diff --git a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php
index b2d85335aa..e1c5ce9978 100644
--- a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php
+++ b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutPublishNode.php
@@ -2,65 +2,28 @@
 
 namespace Drupal\content_moderation\Plugin\Action;
 
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\node\Plugin\Action\PublishNode;
 use Drupal\content_moderation\ModerationInformationInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Alternate action plugin that can opt-out of modifying moderated entities.
  *
- * @see \Drupal\node\Plugin\Action\PublishNode
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish
+ *   instead.
+ *
+ * @see \Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish
+ * @see https://www.drupal.org/node/2919303
  */
-class ModerationOptOutPublishNode extends PublishNode implements ContainerFactoryPluginInterface {
-
-  /**
-   * Moderation information service.
-   *
-   * @var \Drupal\content_moderation\ModerationInformationInterface
-   */
-  protected $moderationInfo;
-
-  /**
-   * ModerationOptOutPublishNode constructor.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
-   *   The moderation information service.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModerationInformationInterface $moderation_info) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->moderationInfo = $moderation_info;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration, $plugin_id, $plugin_definition,
-      $container->get('content_moderation.moderation_information')
-    );
-  }
+class ModerationOptOutPublishNode extends ModerationOptOutPublish {
 
   /**
    * {@inheritdoc}
    */
-  public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\node\NodeInterface $entity */
-    if ($entity && $this->moderationInfo->isModeratedEntity($entity)) {
-      drupal_set_message($this->t("@bundle @label were skipped as they are under moderation and may not be directly published.", ['@bundle' => node_get_type_label($entity), '@label' => $entity->getEntityType()->getPluralLabel()]), 'warning');
-      $result = AccessResult::forbidden();
-      return $return_as_object ? $result : $result->isAllowed();
-    }
-    return parent::access($entity, $account, $return_as_object);
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_info, EntityTypeBundleInfoInterface $bundle_info) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $moderation_info, $bundle_info);
+    @trigger_error(__NAMESPACE__ . '\ModerationOptOutPublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublish.php
similarity index 59%
copy from core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php
copy to core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublish.php
index ee36169468..5a46b3e341 100644
--- a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php
+++ b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublish.php
@@ -3,18 +3,20 @@
 namespace Drupal\content_moderation\Plugin\Action;
 
 use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Action\Plugin\Action\UnpublishAction;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\node\Plugin\Action\UnpublishNode;
 use Drupal\content_moderation\ModerationInformationInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Alternate action plugin that can opt-out of modifying moderated entities.
  *
- * @see \Drupal\node\Plugin\Action\UnpublishNode
+ * @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
  */
-class ModerationOptOutUnpublishNode extends UnpublishNode implements ContainerFactoryPluginInterface {
+class ModerationOptOutUnpublish extends UnpublishAction implements ContainerFactoryPluginInterface {
 
   /**
    * Moderation information service.
@@ -24,7 +26,14 @@ class ModerationOptOutUnpublishNode extends UnpublishNode implements ContainerFa
   protected $moderationInfo;
 
   /**
-   * ModerationOptOutUnpublishNode constructor.
+   * Bundle info service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $bundleInfo;
+
+  /**
+   * ModerationOptOutUnpublish constructor.
    *
    * @param array $configuration
    *   A configuration array containing information about the plugin instance.
@@ -32,12 +41,17 @@ class ModerationOptOutUnpublishNode extends UnpublishNode implements ContainerFa
    *   The plugin_id for the plugin instance.
    * @param mixed $plugin_definition
    *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
    * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
    *   The moderation information service.
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info
+   *   Bundle info service.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModerationInformationInterface $moderation_info) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_info, EntityTypeBundleInfoInterface $bundle_info) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
     $this->moderationInfo = $moderation_info;
+    $this->bundleInfo = $bundle_info;
   }
 
   /**
@@ -46,7 +60,9 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     return new static(
       $configuration, $plugin_id, $plugin_definition,
-      $container->get('content_moderation.moderation_information')
+      $container->get('entity_type.manager'),
+      $container->get('content_moderation.moderation_information'),
+      $container->get('entity_type.bundle.info')
     );
   }
 
@@ -54,9 +70,11 @@ public static function create(ContainerInterface $container, array $configuratio
    * {@inheritdoc}
    */
   public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\node\NodeInterface $entity */
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     if ($entity && $this->moderationInfo->isModeratedEntity($entity)) {
-      drupal_set_message($this->t("@bundle @label were skipped as they are under moderation and may not be directly unpublished.", ['@bundle' => node_get_type_label($entity), '@label' => $entity->getEntityType()->getPluralLabel()]), 'warning');
+      $bundle_info = $this->bundleInfo->getBundleInfo($entity->getEntityTypeId());
+      $bundle_label = $bundle_info[$entity->bundle()]['label'];
+      drupal_set_message($this->t("@bundle @label were skipped as they are under moderation and may not be directly unpublished.", ['@bundle' => $bundle_label, '@label' => $entity->getEntityType()->getPluralLabel()]), 'warning');
       $result = AccessResult::forbidden();
       return $return_as_object ? $result : $result->isAllowed();
     }
diff --git a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php
index ee36169468..d76d3f1832 100644
--- a/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php
+++ b/core/modules/content_moderation/src/Plugin/Action/ModerationOptOutUnpublishNode.php
@@ -2,65 +2,28 @@
 
 namespace Drupal\content_moderation\Plugin\Action;
 
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\node\Plugin\Action\UnpublishNode;
 use Drupal\content_moderation\ModerationInformationInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Alternate action plugin that can opt-out of modifying moderated entities.
  *
- * @see \Drupal\node\Plugin\Action\UnpublishNode
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublish
+ *   instead.
+ *
+ * @see \Drupal\content_moderation\Plugin\Action\ModerationOptOutPublish
+ * @see https://www.drupal.org/node/2919303
  */
-class ModerationOptOutUnpublishNode extends UnpublishNode implements ContainerFactoryPluginInterface {
-
-  /**
-   * Moderation information service.
-   *
-   * @var \Drupal\content_moderation\ModerationInformationInterface
-   */
-  protected $moderationInfo;
-
-  /**
-   * ModerationOptOutUnpublishNode constructor.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\content_moderation\ModerationInformationInterface $moderation_info
-   *   The moderation information service.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModerationInformationInterface $moderation_info) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->moderationInfo = $moderation_info;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration, $plugin_id, $plugin_definition,
-      $container->get('content_moderation.moderation_information')
-    );
-  }
+class ModerationOptOutUnpublishNode extends ModerationOptOutUnpublish {
 
   /**
    * {@inheritdoc}
    */
-  public function access($entity, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\node\NodeInterface $entity */
-    if ($entity && $this->moderationInfo->isModeratedEntity($entity)) {
-      drupal_set_message($this->t("@bundle @label were skipped as they are under moderation and may not be directly unpublished.", ['@bundle' => node_get_type_label($entity), '@label' => $entity->getEntityType()->getPluralLabel()]), 'warning');
-      $result = AccessResult::forbidden();
-      return $return_as_object ? $result : $result->isAllowed();
-    }
-    return parent::access($entity, $account, $return_as_object);
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, ModerationInformationInterface $moderation_info, EntityTypeBundleInfoInterface $bundle_info) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $moderation_info, $bundle_info);
+    @trigger_error(__NAMESPACE__ . '\ModerationOptOutUnpublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublish instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/node/config/install/system.action.node_publish_action.yml b/core/modules/node/config/install/system.action.node_publish_action.yml
index 20ed19b639..3e139bbe6a 100644
--- a/core/modules/node/config/install/system.action.node_publish_action.yml
+++ b/core/modules/node/config/install/system.action.node_publish_action.yml
@@ -6,5 +6,5 @@ dependencies:
 id: node_publish_action
 label: 'Publish content'
 type: node
-plugin: node_publish_action
+plugin: entity:publish_action:node
 configuration: {  }
diff --git a/core/modules/node/config/install/system.action.node_save_action.yml b/core/modules/node/config/install/system.action.node_save_action.yml
index 887ff43d05..1759c35ae2 100644
--- a/core/modules/node/config/install/system.action.node_save_action.yml
+++ b/core/modules/node/config/install/system.action.node_save_action.yml
@@ -6,5 +6,5 @@ dependencies:
 id: node_save_action
 label: 'Save content'
 type: node
-plugin: node_save_action
+plugin: entity:save_action:node
 configuration: {  }
diff --git a/core/modules/node/config/install/system.action.node_unpublish_action.yml b/core/modules/node/config/install/system.action.node_unpublish_action.yml
index 1e778c0dca..0d5e35e9c8 100644
--- a/core/modules/node/config/install/system.action.node_unpublish_action.yml
+++ b/core/modules/node/config/install/system.action.node_unpublish_action.yml
@@ -6,5 +6,5 @@ dependencies:
 id: node_unpublish_action
 label: 'Unpublish content'
 type: node
-plugin: node_unpublish_action
+plugin: entity:unpublish_action:node
 configuration: {  }
diff --git a/core/modules/node/config/schema/node.schema.yml b/core/modules/node/config/schema/node.schema.yml
index 11a93c5310..50b3f3e5ba 100644
--- a/core/modules/node/config/schema/node.schema.yml
+++ b/core/modules/node/config/schema/node.schema.yml
@@ -62,14 +62,20 @@ action.configuration.node_promote_action:
   type: action_configuration_default
   label: 'Promote selected content from front page configuration'
 
+# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2919303
 action.configuration.node_publish_action:
   type: action_configuration_default
   label: 'Publish selected content configuration'
 
+# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2919303
 action.configuration.node_unpublish_action:
   type: action_configuration_default
   label: 'Unpublish selected content configuration'
 
+# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+# @see https://www.drupal.org/node/2919303
 action.configuration.node_save_action:
   type: action_configuration_default
   label: 'Save content configuration'
diff --git a/core/modules/node/src/Plugin/Action/PublishNode.php b/core/modules/node/src/Plugin/Action/PublishNode.php
index 2fa8c019ff..adecc7de88 100644
--- a/core/modules/node/src/Plugin/Action/PublishNode.php
+++ b/core/modules/node/src/Plugin/Action/PublishNode.php
@@ -2,25 +2,32 @@
 
 namespace Drupal\node\Plugin\Action;
 
-use Drupal\Core\Field\FieldUpdateActionBase;
-use Drupal\node\NodeInterface;
+use Drupal\Core\Action\Plugin\Action\PublishAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Publishes a node.
  *
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\Core\Action\Plugin\Action\PublishAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\PublishAction
+ * @see https://www.drupal.org/node/2919303
+ *
  * @Action(
  *   id = "node_publish_action",
  *   label = @Translation("Publish selected content"),
  *   type = "node"
  * )
  */
-class PublishNode extends FieldUpdateActionBase {
+class PublishNode extends PublishAction {
 
   /**
    * {@inheritdoc}
    */
-  protected function getFieldsToUpdate() {
-    return ['status' => NodeInterface::PUBLISHED];
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
+    @trigger_error(__NAMESPACE__ . '\PublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\PublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/node/src/Plugin/Action/SaveNode.php b/core/modules/node/src/Plugin/Action/SaveNode.php
index e358fef36c..d9eea0c921 100644
--- a/core/modules/node/src/Plugin/Action/SaveNode.php
+++ b/core/modules/node/src/Plugin/Action/SaveNode.php
@@ -2,36 +2,33 @@
 
 namespace Drupal\node\Plugin\Action;
 
-use Drupal\Core\Action\ActionBase;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Action\Plugin\Action\SaveAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Provides an action that can save any entity.
  *
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\Core\Action\Plugin\Action\SaveAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\SaveAction
+ * @see https://www.drupal.org/node/2919303
+ *
  * @Action(
  *   id = "node_save_action",
  *   label = @Translation("Save content"),
  *   type = "node"
  * )
  */
-class SaveNode extends ActionBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function execute($entity = NULL) {
-    // We need to change at least one value, otherwise the changed timestamp
-    // will not be updated.
-    $entity->changed = 0;
-    $entity->save();
-  }
+class SaveNode extends SaveAction {
 
   /**
    * {@inheritdoc}
    */
-  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    /** @var \Drupal\node\NodeInterface $object */
-    return $object->access('update', $account, $return_as_object);
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $time);
+    @trigger_error(__NAMESPACE__ . '\SaveNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\SaveAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/node/src/Plugin/Action/UnpublishNode.php b/core/modules/node/src/Plugin/Action/UnpublishNode.php
index 7c286e2fc4..bb188d7009 100644
--- a/core/modules/node/src/Plugin/Action/UnpublishNode.php
+++ b/core/modules/node/src/Plugin/Action/UnpublishNode.php
@@ -2,25 +2,32 @@
 
 namespace Drupal\node\Plugin\Action;
 
-use Drupal\Core\Field\FieldUpdateActionBase;
-use Drupal\node\NodeInterface;
+use Drupal\Core\Action\Plugin\Action\UnpublishAction;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Unpublishes a node.
  *
+ * @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
+ *   Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead.
+ *
+ * @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
+ * @see https://www.drupal.org/node/2919303
+ *
  * @Action(
  *   id = "node_unpublish_action",
  *   label = @Translation("Unpublish selected content"),
  *   type = "node"
  * )
  */
-class UnpublishNode extends FieldUpdateActionBase {
+class UnpublishNode extends UnpublishAction {
 
   /**
    * {@inheritdoc}
    */
-  protected function getFieldsToUpdate() {
-    return ['status' => NodeInterface::NOT_PUBLISHED];
+  public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
+    @trigger_error(__NAMESPACE__ . '\UnpublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
   }
 
 }
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index eb11a19f93..da13077de8 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -88,3 +88,26 @@ function system_post_update_field_type_plugins() {
 function system_post_update_field_formatter_entity_schema() {
   // Empty post-update hook.
 }
+
+/**
+ * Change plugin IDs of actions.
+ */
+function system_post_update_change_action_plugins() {
+  $old_new_action_id_map = [
+    'comment_publish_action' => 'entity:publish_action:comment',
+    'comment_unpublish_action' => 'entity:unpublish_action:comment',
+    'comment_save_action' => 'entity:save_action:comment',
+    'node_publish_action' => 'entity:publish_action:node',
+    'node_unpublish_action' => 'entity:unpublish_action:node',
+    'node_save_action' => 'entity:save_action:node',
+  ];
+
+  /** @var \Drupal\system\Entity\Action[] $actions */
+  $actions = \Drupal::entityTypeManager()->getStorage('action')->loadMultiple();
+  foreach ($actions as $action) {
+    if (isset($old_new_action_id_map[$action->getPlugin()->getPluginId()])) {
+      $action->setPlugin($old_new_action_id_map[$action->getPlugin()->getPluginId()]);
+      $action->save();
+    }
+  }
+}
diff --git a/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php b/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
new file mode 100644
index 0000000000..4094d00313
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Update/UpdateActionsWithEntityPluginsTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+use Drupal\system\Entity\Action;
+
+/**
+ * Tests upgrading comment and node actions to generic entity ones.
+ *
+ * @group Update
+ */
+class UpdateActionsWithEntityPluginsTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz'];
+  }
+
+  /**
+   * Tests upgrading comment and node actions to generic entity ones.
+   *
+   * @see system_post_update_change_action_plugins()
+   */
+  public function testUpdateActionsWithEntityPlugins() {
+    $old_new_action_id_map = [
+      'comment_publish_action' => ['comment_publish_action', 'entity:publish_action:comment'],
+      'comment_unpublish_action' => ['comment_unpublish_action', 'entity:unpublish_action:comment'],
+      'comment_save_action' => ['comment_save_action', 'entity:save_action:comment'],
+      'node_publish_action' => ['node_publish_action', 'entity:publish_action:node'],
+      'node_unpublish_action' => ['node_unpublish_action', 'entity:unpublish_action:node'],
+      'node_save_action' => ['node_save_action', 'entity:save_action:node'],
+    ];
+
+    foreach ($old_new_action_id_map as $key => list($before, $after)) {
+      $config = \Drupal::configFactory()->get('system.action.' . $key);
+      $this->assertSame($before, $config->get('plugin'));
+    }
+
+    $this->runUpdates();
+
+    foreach ($old_new_action_id_map as $key => list($before, $after)) {
+      /** @var \Drupal\system\Entity\Action $action */
+      $action = Action::load($key);
+      $this->assertSame($after, $action->getPlugin()->getPluginId());
+      $config = \Drupal::configFactory()->get('system.action.' . $key);
+      $this->assertSame($after, $config->get('plugin'));
+
+      // Check that the type the action is based on will be a module dependency.
+      $this->assertArraySubset(['module' => [$action->getPluginDefinition()['type']]], $action->getDependencies());
+    }
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php b/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php
new file mode 100644
index 0000000000..04bf1c053e
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Action/PublishActionTest.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Action;
+
+use Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver;
+use Drupal\entity_test\Entity\EntityTestMulRevPub;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\system\Entity\Action;
+
+/**
+ * @group Action
+ */
+class PublishActionTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'entity_test', 'user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('entity_test_mulrevpub');
+  }
+
+  /**
+   * @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver::getDerivativeDefinitions
+   */
+  public function testGetDerivativeDefinitions() {
+    $deriver = new EntityPublishedActionDeriver(\Drupal::entityTypeManager());
+    $this->assertArraySubset([
+      'entity_test_mulrevpub' => [
+        'type' => 'entity_test_mulrevpub',
+        'label' => 'Save test entity - revisions, data table, and published interface',
+        'action_label' => 'Save',
+      ],
+    ], $deriver->getDerivativeDefinitions([
+      'action_label' => 'Save',
+    ]));
+  }
+
+  /**
+   * @covers \Drupal\Core\Action\Plugin\Action\PublishAction::execute
+   */
+  public function testPublishAction() {
+    $entity = EntityTestMulRevPub::create(['name' => 'test']);
+    $entity->setUnpublished()->save();
+
+    $action = Action::create([
+      'id' => 'entity_publish_action',
+      'plugin' => 'entity:publish_action:entity_test_mulrevpub',
+    ]);
+    $action->save();
+    $this->assertFalse($entity->isPublished());
+    $action->execute([$entity]);
+    $this->assertTrue($entity->isPublished());
+    $this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
+  }
+
+  /**
+   * @covers \Drupal\Core\Action\Plugin\Action\UnpublishAction::execute
+   */
+  public function testUnpublishAction() {
+    $entity = EntityTestMulRevPub::create(['name' => 'test']);
+    $entity->setPublished()->save();
+
+    $action = Action::create([
+      'id' => 'entity_unpublish_action',
+      'plugin' => 'entity:unpublish_action:entity_test_mulrevpub',
+    ]);
+    $action->save();
+    $this->assertTrue($entity->isPublished());
+    $action->execute([$entity]);
+    $this->assertFalse($entity->isPublished());
+    $this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php b/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php
new file mode 100644
index 0000000000..118f5428d8
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Action/SaveActionTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\KernelTests\Core\Action;
+
+use Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver;
+use Drupal\entity_test\Entity\EntityTestMulChanged;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\system\Entity\Action;
+
+/**
+ * @group Action
+ */
+class SaveActionTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'entity_test', 'user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('entity_test_mul_changed');
+  }
+
+  /**
+   * @covers \Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver::getDerivativeDefinitions
+   */
+  public function testGetDerivativeDefinitions() {
+    $deriver = new EntityChangedActionDeriver(\Drupal::entityTypeManager());
+    $this->assertArraySubset([
+      'entity_test_mul_changed' => [
+        'type' => 'entity_test_mul_changed',
+        'label' => 'Save test entity - data table',
+        'action_label' => 'Save',
+      ],
+    ], $deriver->getDerivativeDefinitions([
+      'action_label' => 'Save',
+    ]));
+  }
+
+  /**
+   * @covers \Drupal\Core\Action\Plugin\Action\SaveAction::execute
+   */
+  public function testSaveAction() {
+    $entity = EntityTestMulChanged::create(['name' => 'test']);
+    $entity->save();
+    $saved_time = $entity->getChangedTime();
+
+    $action = Action::create([
+      'id' => 'entity_save_action',
+      'plugin' => 'entity:save_action:entity_test_mul_changed',
+    ]);
+    $action->save();
+    $action->execute([$entity]);
+    $this->assertNotSame($saved_time, $entity->getChangedTime());
+    $this->assertArraySubset(['module' => ['entity_test']], $action->getDependencies());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index 9ec5430697..2eecec9756 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -135,6 +135,12 @@ public static function getSkippedDeprecations() {
       'drupal_get_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931',
       'Adding or retrieving messages prior to the container being initialized was deprecated in Drupal 8.5.0 and this functionality will be removed before Drupal 9.0.0. Please report this usage at https://www.drupal.org/node/2928994.',
       'The "serializer.normalizer.file_entity.hal" normalizer service is deprecated: it is obsolete, it only remains available for backwards compatibility.',
+      'Drupal\comment\Plugin\Action\PublishComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\PublishAction instead. See https://www.drupal.org/node/2919303.',
+      'Drupal\comment\Plugin\Action\SaveComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\SaveAction instead. See https://www.drupal.org/node/2919303.',
+      'Drupal\comment\Plugin\Action\UnpublishComment is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead. See https://www.drupal.org/node/2919303.',
+      'Drupal\node\Plugin\Action\PublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\PublishAction instead. See https://www.drupal.org/node/2919303.',
+      'Drupal\node\Plugin\Action\SaveNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\SaveAction instead. See https://www.drupal.org/node/2919303.',
+      'Drupal\node\Plugin\Action\UnpublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead. See https://www.drupal.org/node/2919303.',
     ];
   }
 
