diff --git a/dvc/dvcfile.py b/dvc/dvcfile.py
--- a/dvc/dvcfile.py
+++ b/dvc/dvcfile.py
@@ -208,6 +208,8 @@ def _dump_pipeline_file(self, stage):
 
         if existing_entry:
             orig_stage_data = data["stages"][stage.name]
+            if "meta" in orig_stage_data:
+                stage_data[stage.name]["meta"] = orig_stage_data["meta"]
             apply_diff(stage_data[stage.name], orig_stage_data)
         else:
             data["stages"].update(stage_data)

diff --git a/tests/func/test_dvcfile.py b/tests/func/test_dvcfile.py
--- a/tests/func/test_dvcfile.py
+++ b/tests/func/test_dvcfile.py
@@ -290,3 +290,37 @@ def test_remove_stage_preserves_comment(tmp_dir, dvc, run_copy):
         "# This copies 'foo' text to 'foo' file."
         in (tmp_dir / PIPELINE_FILE).read_text()
     )
+
+
+def test_dvcfile_dump_preserves_meta(tmp_dir, dvc, run_copy):
+    tmp_dir.gen("foo", "foo")
+    stage = run_copy("foo", "bar", name="run_copy")
+    dvcfile = stage.dvcfile
+
+    data = dvcfile._load()[0]
+    metadata = {"name": "copy-file"}
+    data["stages"]["run_copy"]["meta"] = metadata
+    dump_yaml(dvcfile.path, data)
+
+    dvcfile.dump(stage, update_pipeline=True)
+    assert dvcfile._load()[0] == data
+    assert dvcfile._load()[0]["stages"]["run_copy"]["meta"] == metadata
+
+
+def test_dvcfile_dump_preserves_comments(tmp_dir, dvc):
+    text = textwrap.dedent(
+        """\
+        stages:
+          generate-foo:
+            cmd: echo foo > foo
+            # This copies 'foo' text to 'foo' file.
+            outs:
+            - foo"""
+    )
+    tmp_dir.gen("dvc.yaml", text)
+    stage = dvc.get_stage(name="generate-foo")
+    stage.outs[0].use_cache = False
+    dvcfile = stage.dvcfile
+
+    dvcfile.dump(stage, update_pipeline=True)
+    assert dvcfile._load()[1] == (text + ":\n\tcache: false\n".expandtabs())

`plots modify` is not preserving `meta`
I think, I implemented this. But, somehow it's not working. Also need to take a look if comments are preserved.

tests/func/test_dvcfile.py::test_dvcfile_dump_preserves_meta
tests/func/test_dvcfile.py::test_run_load_one_for_multistage tests/func/test_dvcfile.py::test_run_load_one_for_multistage_non_existing tests/func/test_dvcfile.py::test_run_load_one_for_multistage_non_existing_stage_name tests/func/test_dvcfile.py::test_run_load_one_on_single_stage tests/func/test_dvcfile.py::test_has_stage_with_name tests/func/test_dvcfile.py::test_load_all_multistage tests/func/test_dvcfile.py::test_load_all_singlestage tests/func/test_dvcfile.py::test_load_singlestage tests/func/test_dvcfile.py::test_try_get_single_stage_from_pipeline_file tests/func/test_dvcfile.py::test_stage_collection tests/func/test_dvcfile.py::test_remove_stage tests/func/test_dvcfile.py::test_remove_stage_lockfile tests/func/test_dvcfile.py::test_remove_stage_dvcfiles tests/func/test_dvcfile.py::test_remove_stage_on_lockfile_format_error tests/func/test_dvcfile.py::test_remove_stage_preserves_comment tests/func/test_dvcfile.py::test_dvcfile_dump_preserves_comments