diff --git a/lxd/instance.go b/lxd/instance.go
--- a/lxd/instance.go
+++ b/lxd/instance.go
@@ -196,6 +196,114 @@ type instanceCreateAsCopyOpts struct {
 	allowInconsistent    bool              // Ignore some copy errors
 }
 
+func projectProfileNames(profiles []api.Profile) []string {
+	names := make([]string, 0, len(profiles))
+	for _, profile := range profiles {
+		names = append(names, profile.Name)
+	}
+
+	return names
+}
+
+func instancePlacementRequest(inst instance.Instance, target db.InstanceArgs) api.InstancesPost {
+	config := make(map[string]string, len(target.Config))
+	for key, value := range target.Config {
+		config[key] = value
+	}
+
+	return api.InstancesPost{
+		InstancePut: api.InstancePut{
+			Config:   config,
+			Devices:  target.Devices.CloneNative(),
+			Profiles: projectProfileNames(target.Profiles),
+		},
+		Name:   target.Name,
+		Type:   api.InstanceType(inst.Type().String()),
+		Source: api.InstanceSource{Type: "copy"},
+	}
+}
+
+func checkTargetSnapshots(info *project.ProjectInfo, opts instanceCreateAsCopyOpts) error {
+	snapshots, err := opts.sourceInstance.Snapshots()
+	if err != nil {
+		return err
+	}
+
+	if len(snapshots) == 0 {
+		return nil
+	}
+
+	err = project.AllowSnapshotCreation(&info.Project)
+	if err != nil {
+		return fmt.Errorf(
+			"Instance snapshots cannot be placed in project %q: %w",
+			opts.targetInstance.Project,
+			err,
+		)
+	}
+
+	for _, snapshot := range snapshots {
+		_, name, _ := api.GetParentAndSnapshotName(snapshot.Name())
+		target := db.InstanceArgs{
+			Config:   snapshot.LocalConfig(),
+			Devices:  snapshot.LocalDevices(),
+			Name:     opts.targetInstance.Name + shared.SnapshotDelimiter + name,
+			Profiles: snapshot.Profiles(),
+		}
+		req := instancePlacementRequest(snapshot, target)
+		err = project.AllowInstanceCreation(*info, req)
+		if err != nil {
+			return fmt.Errorf(
+				"Snapshot %q cannot be placed in project %q: %w",
+				snapshot.Name(),
+				opts.targetInstance.Project,
+				err,
+			)
+		}
+	}
+
+	return nil
+}
+
+func checkTargetProjectRestrictions(s *state.State, opts instanceCreateAsCopyOpts) error {
+	if opts.sourceInstance.Project().Name == opts.targetInstance.Project {
+		return nil
+	}
+
+	var restrictions *project.ProjectInfo
+	err := s.DB.Cluster.Transaction(context.TODO(), func(
+		ctx context.Context,
+		tx *db.ClusterTx,
+	) error {
+		var err error
+		restrictions, err = project.FetchProject(tx, opts.targetInstance.Project, true)
+		return err
+	})
+	if err != nil {
+		return err
+	}
+
+	if restrictions == nil {
+		return nil
+	}
+
+	req := instancePlacementRequest(opts.sourceInstance, opts.targetInstance)
+	err = project.AllowInstanceCreation(*restrictions, req)
+	if err != nil {
+		return fmt.Errorf(
+			"Instance cannot be placed in project %q: %w",
+			opts.targetInstance.Project,
+			err,
+		)
+	}
+
+	if opts.instanceOnly {
+		return nil
+	}
+
+	return checkTargetSnapshots(restrictions, opts)
+}
+
 // instanceCreateAsCopy create a new instance by copying from an existing instance.
 func instanceCreateAsCopy(s *state.State, opts instanceCreateAsCopyOpts, op *operations.Operation) (instance.Instance, error) {
 	var inst instance.Instance
@@ -203,6 +311,11 @@ func instanceCreateAsCopy(s *state.State, opts instanceCreateAsCopyOpts, op *ope
 	var err error
 	var cleanup revert.Hook
 
+	err = checkTargetProjectRestrictions(s, opts)
+	if err != nil {
+		return nil, err
+	}
+
 	revert := revert.New()
 	defer revert.Fail()
 
diff --git a/lxd/instances_post.go b/lxd/instances_post.go
--- a/lxd/instances_post.go
+++ b/lxd/instances_post.go
@@ -665,1 +665,8 @@ func createFromBackup(s *state.State, r *http.Request, projectName string, pool
+			if len(bInfo.Config.Snapshots) > 0 {
+				err = project.AllowSnapshotCreation(&restrictions.Project)
+				if err != nil {
+					return err
+				}
+			}
+
 			for i, snapshot := range bInfo.Config.Snapshots {
