diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -580,6 +580,24 @@ def construct(cls: Type['Model'], _fields_set: Optional['SetStr'] = None, **valu
         m._init_private_attributes()
         return m
 
+    def _copy_and_set_values(self: 'Model', values: 'DictStrAny', fields_set: 'SetStr', *, deep: bool) -> 'Model':
+        if deep:
+            # chances of having empty dict here are quite low for using smart_deepcopy
+            values = deepcopy(values)
+
+        cls = self.__class__
+        m = cls.__new__(cls)
+        object_setattr(m, '__dict__', values)
+        object_setattr(m, '__fields_set__', fields_set)
+        for name in self.__private_attributes__:
+            value = getattr(self, name, Undefined)
+            if value is not Undefined:
+                if deep:
+                    value = deepcopy(value)
+                object_setattr(m, name, value)
+
+        return m
+
     def copy(
         self: 'Model',
         *,
@@ -599,32 +617,18 @@ def copy(
         :return: new model instance
         """
 
-        v = dict(
+        values = dict(
             self._iter(to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False),
             **(update or {}),
         )
 
-        if deep:
-            # chances of having empty dict here are quite low for using smart_deepcopy
-            v = deepcopy(v)
-
-        cls = self.__class__
-        m = cls.__new__(cls)
-        object_setattr(m, '__dict__', v)
         # new `__fields_set__` can have unset optional fields with a set value in `update` kwarg
         if update:
             fields_set = self.__fields_set__ | update.keys()
         else:
             fields_set = set(self.__fields_set__)
-        object_setattr(m, '__fields_set__', fields_set)
-        for name in self.__private_attributes__:
-            value = getattr(self, name, Undefined)
-            if value is not Undefined:
-                if deep:
-                    value = deepcopy(value)
-                object_setattr(m, name, value)
 
-        return m
+        return self._copy_and_set_values(values, fields_set, deep=deep)
 
     @classmethod
     def schema(cls, by_alias: bool = True, ref_template: str = default_ref_template) -> 'DictStrAny':
@@ -652,7 +656,10 @@ def __get_validators__(cls) -> 'CallableGenerator':
     @classmethod
     def validate(cls: Type['Model'], value: Any) -> 'Model':
         if isinstance(value, cls):
-            return value.copy() if cls.__config__.copy_on_model_validation else value
+            if cls.__config__.copy_on_model_validation:
+                return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=False)
+            else:
+                return value
 
         value = cls._enforce_dict_if_root(value)
 

diff --git a/tests/test_main.py b/tests/test_main.py
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -16,7 +16,9 @@
     Field,
     NoneBytes,
     NoneStr,
+    PrivateAttr,
     Required,
+    SecretStr,
     ValidationError,
     constr,
     root_validator,
@@ -1504,6 +1506,45 @@ class Config:
     assert Model.__fields__['b'].field_info.exclude == {'foo': ..., 'bar': ...}
 
 
+def test_model_exclude_copy_on_model_validation():
+    """When `Config.copy_on_model_validation` is set, it should keep private attributes and excluded fields"""
+
+    class User(BaseModel):
+        _priv: int = PrivateAttr()
+        id: int
+        username: str
+        password: SecretStr = Field(exclude=True)
+        hobbies: List[str]
+
+    my_user = User(id=42, username='JohnDoe', password='hashedpassword', hobbies=['scuba diving'])
+
+    my_user._priv = 13
+    assert my_user.id == 42
+    assert my_user.password.get_secret_value() == 'hashedpassword'
+    assert my_user.dict() == {'id': 42, 'username': 'JohnDoe', 'hobbies': ['scuba diving']}
+
+    class Transaction(BaseModel):
+        id: str
+        user: User = Field(..., exclude={'username'})
+        value: int
+
+        class Config:
+            fields = {'value': {'exclude': True}}
+
+    t = Transaction(
+        id='1234567890',
+        user=my_user,
+        value=9876543210,
+    )
+
+    assert t.user is not my_user
+    assert t.user.hobbies == ['scuba diving']
+    assert t.user.hobbies is my_user.hobbies  # `Config.copy_on_model_validation` only does a shallow copy
+    assert t.user._priv == 13
+    assert t.user.password.get_secret_value() == 'hashedpassword'
+    assert t.dict() == {'id': '1234567890', 'user': {'id': 42, 'hobbies': ['scuba diving']}}
+
+
 @pytest.mark.parametrize(
     'kinds',
     [

Field(..., exclude=True) are missing from nested BaseModel objects
### Checks

* [X] I added a descriptive title to this issue
* [X] I have searched (google, github) for similar issues and couldn't find anything
* [X] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.8.2
            pydantic compiled: False
                 install path: /home/venv/lib/python3.7/site-packages/pydantic
               python version: 3.7.10 (default, May  4 2021, 13:51:36)  [GCC 11.1.1 20210428 (Red Hat 11.1.1-1)]
                     platform: Linux-4.4.0-19041-Microsoft-x86_64-with-fedora-34-Thirty_Four
     optional deps. installed: ['email-validator', 'typing-extensions']
```
<!-- or if you're using pydantic prior to v1.3, manually include: OS, python version and pydantic version -->

<!-- Please read the [docs](https://pydantic-docs.helpmanual.io/) and search through issues to
confirm your bug hasn't already been reported. -->

<!-- Where possible please include a self-contained code snippet describing your bug: -->

```py
from pydantic import BaseModel, Field, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr = Field(..., exclude=True)


class Transaction(BaseModel):
    id: str
    user: User = Field(..., exclude={'username'})
    value: int

    class Config:
        fields = {'value': {'exclude': True}}


t = Transaction(
    id='1234567890',
    user=User(
        id=42,
        username='JohnDoe',
        password='hashedpassword'
    ),
    value=9876543210,
)

print(t.user.password)
print(t.dict())
```

Hi, 
I'm currently interested by the exclude parameter (tested from commit 45db4ad3aa558879824a91dd3b011d0449eb2977).
After BaseModel initialization, `excluded` fields are missing from nested objects.
the BaseModel __init__ uses the `validate()` function that uses `copy()` ( and -> _iter) under the hood. 
https://github.com/samuelcolvin/pydantic/blob/45db4ad3aa558879824a91dd3b011d0449eb2977/pydantic/main.py#L652-L656

as a result, all excluded fields are missing after validation. 
I do not know if this behavior is intended. 
But as far as I can see the exclude should only affect exports function, right ? 

my issue looks in contradiction with this comment bellow. let me know if I misinterpreted the exclude purpose.
https://github.com/samuelcolvin/pydantic/issues/830#issuecomment-842313022

thank you very much 

tests/test_main.py::test_model_exclude_copy_on_model_validation
tests/test_main.py::test_success tests/test_main.py::test_ultra_simple_missing tests/test_main.py::test_ultra_simple_failed tests/test_main.py::test_ultra_simple_repr tests/test_main.py::test_default_factory_field tests/test_main.py::test_default_factory_no_type_field tests/test_main.py::test_comparing tests/test_main.py::test_nullable_strings_success tests/test_main.py::test_nullable_strings_fails tests/test_main.py::test_recursion tests/test_main.py::test_recursion_fails tests/test_main.py::test_not_required tests/test_main.py::test_infer_type tests/test_main.py::test_allow_extra tests/test_main.py::test_forbidden_extra_success tests/test_main.py::test_forbidden_extra_fails tests/test_main.py::test_disallow_mutation tests/test_main.py::test_extra_allowed tests/test_main.py::test_extra_ignored tests/test_main.py::test_set_attr tests/test_main.py::test_set_attr_invalid tests/test_main.py::test_any tests/test_main.py::test_alias tests/test_main.py::test_population_by_field_name tests/test_main.py::test_field_order tests/test_main.py::test_required tests/test_main.py::test_mutability tests/test_main.py::test_immutability[False-False] tests/test_main.py::test_immutability[False-True] tests/test_main.py::test_immutability[True-True] tests/test_main.py::test_not_frozen_are_not_hashable tests/test_main.py::test_with_declared_hash tests/test_main.py::test_frozen_with_hashable_fields_are_hashable tests/test_main.py::test_frozen_with_unhashable_fields_are_not_hashable tests/test_main.py::test_hash_function_give_different_result_for_different_object tests/test_main.py::test_const_validates tests/test_main.py::test_const_uses_default tests/test_main.py::test_const_validates_after_type_validators tests/test_main.py::test_const_with_wrong_value tests/test_main.py::test_const_with_validator tests/test_main.py::test_const_list tests/test_main.py::test_const_list_with_wrong_value tests/test_main.py::test_const_validation_json_serializable tests/test_main.py::test_validating_assignment_pass tests/test_main.py::test_validating_assignment_fail tests/test_main.py::test_validating_assignment_pre_root_validator_fail tests/test_main.py::test_validating_assignment_post_root_validator_fail tests/test_main.py::test_root_validator_many_values_change tests/test_main.py::test_enum_values tests/test_main.py::test_literal_enum_values tests/test_main.py::test_enum_raw tests/test_main.py::test_set_tuple_values tests/test_main.py::test_default_copy tests/test_main.py::test_arbitrary_type_allowed_validation_success tests/test_main.py::test_arbitrary_type_allowed_validation_fails tests/test_main.py::test_arbitrary_types_not_allowed tests/test_main.py::test_type_type_validation_success tests/test_main.py::test_type_type_subclass_validation_success tests/test_main.py::test_type_type_validation_fails_for_instance tests/test_main.py::test_type_type_validation_fails_for_basic_type tests/test_main.py::test_bare_type_type_validation_success tests/test_main.py::test_bare_type_type_validation_fails tests/test_main.py::test_annotation_field_name_shadows_attribute tests/test_main.py::test_value_field_name_shadows_attribute tests/test_main.py::test_class_var tests/test_main.py::test_fields_set tests/test_main.py::test_exclude_unset_dict tests/test_main.py::test_exclude_unset_recursive tests/test_main.py::test_dict_exclude_unset_populated_by_alias tests/test_main.py::test_dict_exclude_unset_populated_by_alias_with_extra tests/test_main.py::test_exclude_defaults tests/test_main.py::test_dir_fields tests/test_main.py::test_dict_with_extra_keys tests/test_main.py::test_root tests/test_main.py::test_root_list tests/test_main.py::test_root_nested tests/test_main.py::test_encode_nested_root tests/test_main.py::test_root_failed tests/test_main.py::test_root_undefined_failed tests/test_main.py::test_parse_root_as_mapping tests/test_main.py::test_parse_obj_non_mapping_root tests/test_main.py::test_parse_obj_nested_root tests/test_main.py::test_untouched_types tests/test_main.py::test_custom_types_fail_without_keep_untouched tests/test_main.py::test_model_iteration tests/test_main.py::test_model_export_nested_list[excluding tests/test_main.py::test_model_export_nested_list[should tests/test_main.py::test_model_export_nested_list[using tests/test_main.py::test_model_export_dict_exclusion[excluding tests/test_main.py::test_model_export_dict_exclusion[using tests/test_main.py::test_model_export_dict_exclusion[exclude tests/test_main.py::test_model_exclude_config_field_merging tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds0] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds1] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds2] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds3] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds4] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds5] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[None-expected0-kinds6] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds0] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds1] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds2] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds3] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds4] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds5] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude1-expected1-kinds6] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds0] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds1] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds2] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds3] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds4] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds5] tests/test_main.py::test_model_export_exclusion_with_fields_and_config[exclude2-expected2-kinds6] tests/test_main.py::test_model_export_exclusion_inheritance tests/test_main.py::test_model_export_with_true_instead_of_ellipsis tests/test_main.py::test_model_export_inclusion tests/test_main.py::test_model_export_inclusion_inheritance tests/test_main.py::test_custom_init_subclass_params tests/test_main.py::test_update_forward_refs_does_not_modify_module_dict tests/test_main.py::test_two_defaults tests/test_main.py::test_default_factory tests/test_main.py::test_default_factory_called_once tests/test_main.py::test_default_factory_called_once_2 tests/test_main.py::test_default_factory_validate_children tests/test_main.py::test_default_factory_parse tests/test_main.py::test_none_min_max_items tests/test_main.py::test_reuse_same_field tests/test_main.py::test_base_config_type_hinting tests/test_main.py::test_allow_mutation_field tests/test_main.py::test_repr_field tests/test_main.py::test_inherited_model_field_copy tests/test_main.py::test_inherited_model_field_untouched tests/test_main.py::test_mapping_retains_type_subclass tests/test_main.py::test_mapping_retains_type_defaultdict tests/test_main.py::test_mapping_retains_type_fallback_error tests/test_main.py::test_typing_coercion_dict tests/test_main.py::test_typing_coercion_defaultdict tests/test_main.py::test_typing_coercion_counter tests/test_main.py::test_typing_counter_value_validation tests/test_main.py::test_class_kwargs_config tests/test_main.py::test_class_kwargs_config_json_encoders tests/test_main.py::test_class_kwargs_config_and_attr_conflict tests/test_main.py::test_class_kwargs_custom_config