diff --git a/commands/compile/compile.go b/commands/compile/compile.go
index ba904fddad0..92858dd68d6 100644
--- a/commands/compile/compile.go
+++ b/commands/compile/compile.go
@@ -81,7 +81,11 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
 
 	fqbnIn := req.GetFqbn()
 	if fqbnIn == "" && sk != nil {
-		fqbnIn = sk.GetDefaultFQBN()
+		if pme.GetProfile() != nil {
+			fqbnIn = pme.GetProfile().FQBN
+		} else {
+			fqbnIn = sk.GetDefaultFQBN()
+		}
 	}
 	if fqbnIn == "" {
 		return nil, &arduino.MissingFQBNError{}
diff --git a/commands/upload/upload.go b/commands/upload/upload.go
index 75f8aae9238..aa74023b0e3 100644
--- a/commands/upload/upload.go
+++ b/commands/upload/upload.go
@@ -143,12 +143,17 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
 	}
 	defer pmeRelease()
 
+	fqbn := req.GetFqbn()
+	if fqbn == "" && pme.GetProfile() != nil {
+		fqbn = pme.GetProfile().FQBN
+	}
+
 	updatedPort, err := runProgramAction(
 		pme,
 		sk,
 		req.GetImportFile(),
 		req.GetImportDir(),
-		req.GetFqbn(),
+		fqbn,
 		req.GetPort(),
 		req.GetProgrammer(),
 		req.GetVerbose(),
diff --git a/docs/sketch-project-file.md b/docs/sketch-project-file.md
index 01a5607c8db..640ede7505e 100644
--- a/docs/sketch-project-file.md
+++ b/docs/sketch-project-file.md
@@ -95,6 +95,8 @@ profiles:
       - ArduinoIoTCloud (1.0.2)
       - Arduino_ConnectionHandler (0.6.4)
       - TinyDHT sensor library (1.1.0)
+
+default_profile: nanorp
 ```
 
 ### Building a sketch
@@ -116,6 +118,16 @@ not be used in any way. In other words, the build is isolated from the system an
 specified in the profile: this will ensure that the build is portable and reproducible independently from the platforms
 and libraries installed in the system.
 
+### Using a default profile
+
+If a `default_profile` is specified in the `sketch.yaml` then the “classic” compile command:
+
+```
+arduino-cli compile [sketch]
+```
+
+will, instead, trigger a profile-based build using the default profile indicated in the `sketch.yaml`.
+
 ## Default flags for Arduino CLI usage
 
 The sketch project file may be used to set the default value for some command line flags of the Arduino CLI, in
@@ -124,6 +136,7 @@ particular:
 - The `default_fqbn` key sets the default value for the `--fqbn` flag
 - The `default_port` key sets the default value for the `--port` flag
 - The `default_protocol` key sets the default value for the `--protocol` flag
+- The `default_profile` key sets the default value for the `--profile` flag
 
 For example:
 
@@ -131,8 +144,9 @@ For example:
 default_fqbn: arduino:avr:uno
 default_port: /dev/ttyACM0
 default_protocol: serial
+default_profile: myprofile
 ```
 
-With this configuration set, it is not necessary to specify the `--fqbn`, `--port`, or `--protocol` flags to the
-[`arduino-cli compile`](commands/arduino-cli_compile.md) or [`arduino-cli upload`](commands/arduino-cli_upload.md)
+With this configuration set, it is not necessary to specify the `--fqbn`, `--port`, `--protocol` or `--profile` flags to
+the [`arduino-cli compile`](commands/arduino-cli_compile.md) or [`arduino-cli upload`](commands/arduino-cli_upload.md)
 commands when compiling or uploading the sketch.
diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go
index baa1dff5f89..dfd5d4c70ba 100644
--- a/internal/cli/compile/compile.go
+++ b/internal/cli/compile/compile.go
@@ -159,15 +159,25 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
 	}
 
 	sketchPath := arguments.InitSketchPath(path)
-	inst, profile := instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath)
-	if fqbnArg.String() == "" {
-		fqbnArg.Set(profile.GetFqbn())
-	}
 
 	sk, err := sketch.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()})
 	if err != nil {
 		feedback.FatalError(err, feedback.ErrGeneric)
 	}
+
+	var inst *rpc.Instance
+	var profile *rpc.Profile
+
+	if profileArg.Get() == "" {
+		inst, profile = instance.CreateAndInitWithProfile(sk.GetDefaultProfile().GetName(), sketchPath)
+	} else {
+		inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath)
+	}
+
+	if fqbnArg.String() == "" {
+		fqbnArg.Set(profile.GetFqbn())
+	}
+
 	fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol())
 
 	if keysKeychain != "" || signKey != "" || encryptKey != "" {
diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go
index 1fa91086ac0..0f2a798d979 100644
--- a/internal/cli/upload/upload.go
+++ b/internal/cli/upload/upload.go
@@ -95,17 +95,25 @@ func runUploadCommand(command *cobra.Command, args []string) {
 		feedback.Fatal(tr("Error during Upload: %v", err), feedback.ErrGeneric)
 	}
 
-	instance, profile := instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath)
+	var inst *rpc.Instance
+	var profile *rpc.Profile
+
+	if profileArg.Get() == "" {
+		inst, profile = instance.CreateAndInitWithProfile(sk.Project.DefaultProfile, sketchPath)
+	} else {
+		inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath)
+	}
+
 	if fqbnArg.String() == "" {
 		fqbnArg.Set(profile.GetFqbn())
 	}
 
 	defaultFQBN := sk.GetDefaultFQBN()
 	defaultAddress, defaultProtocol := sk.GetDefaultPortAddressAndProtocol()
-	fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, defaultFQBN, defaultAddress, defaultProtocol)
+	fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, defaultFQBN, defaultAddress, defaultProtocol)
 
 	userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{
-		Instance: instance,
+		Instance: inst,
 		Fqbn:     fqbn,
 		Protocol: port.Protocol,
 	})
@@ -122,7 +130,7 @@ func runUploadCommand(command *cobra.Command, args []string) {
 			}
 
 			// FIXME: Here we must not access package manager...
-			pme, release := commands.GetPackageManagerExplorer(&rpc.UploadRequest{Instance: instance})
+			pme, release := commands.GetPackageManagerExplorer(&rpc.UploadRequest{Instance: inst})
 			platform := pme.FindPlatform(&packagemanager.PlatformReference{
 				Package:              split[0],
 				PlatformArchitecture: split[1],
@@ -156,7 +164,7 @@ func runUploadCommand(command *cobra.Command, args []string) {
 
 	stdOut, stdErr, stdIOResult := feedback.OutputStreams()
 	req := &rpc.UploadRequest{
-		Instance:   instance,
+		Instance:   inst,
 		Fqbn:       fqbn,
 		SketchPath: path,
 		Port:       port,
diff --git a/internal/integrationtest/profiles/profiles_test.go b/internal/integrationtest/profiles/profiles_test.go
index 9cd1ffcb556..4915bed5cf7 100644
--- a/internal/integrationtest/profiles/profiles_test.go
+++ b/internal/integrationtest/profiles/profiles_test.go
@@ -20,6 +20,7 @@ import (
 
 	"github.com/arduino/arduino-cli/internal/integrationtest"
 	"github.com/stretchr/testify/require"
+	"go.bug.st/testifyjson/requirejson"
 )
 
 func TestCompileWithProfiles(t *testing.T) {
@@ -69,3 +70,83 @@ func TestBuilderDidNotCatchLibsFromUnusedPlatforms(t *testing.T) {
 	require.NotContains(t, string(stdout), "samd")
 	require.NotContains(t, string(stderr), "samd")
 }
+
+func TestCompileWithDefaultProfile(t *testing.T) {
+	env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
+	defer env.CleanUp()
+
+	// Init the environment explicitly
+	_, _, err := cli.Run("core", "update-index")
+	require.NoError(t, err)
+
+	// Installa core/libs globally
+	_, _, err = cli.Run("core", "install", "arduino:avr@1.8.3")
+	require.NoError(t, err)
+
+	// copy sketch_with_profile into the working directory
+	sketchWithoutDefProfilePath := cli.CopySketch("sketch_without_default_profile")
+	sketchWithDefProfilePath := cli.CopySketch("sketch_with_default_profile")
+
+	{
+		// no default profile -> error missing FQBN
+		_, _, err := cli.Run("compile", sketchWithoutDefProfilePath.String(), "--format", "json")
+		require.Error(t, err)
+	}
+	{
+		// specified fbqn -> compile with specified FQBN and use global installation
+		stdout, _, err := cli.Run("compile", "-b", "arduino:avr:nano", sketchWithoutDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.3"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`)
+	}
+	{
+		// specified profile -> use the specified profile
+		stdout, _, err := cli.Run("compile", "--profile", "avr1", sketchWithoutDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:uno" ]`)
+	}
+	{
+		// specified profile and fqbn -> use the specified profile and override fqbn
+		stdout, _, err := cli.Run("compile", "--profile", "avr1", "-b", "arduino:avr:nano", sketchWithoutDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`)
+	}
+
+	{
+		// default profile -> use default profile
+		stdout, _, err := cli.Run("compile", sketchWithDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.5"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:leonardo" ]`)
+	}
+	{
+		// default profile, specified fbqn -> use default profile, override fqbn
+		stdout, _, err := cli.Run("compile", "-b", "arduino:avr:nano", sketchWithDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.5"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`)
+	}
+	{
+		// default profile, specified different profile -> use the specified profile
+		stdout, _, err := cli.Run("compile", "--profile", "avr1", sketchWithDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:uno" ]`)
+	}
+	{
+		// default profile, specified different profile and fqbn -> use the specified profile and override fqbn
+		stdout, _, err := cli.Run("compile", "--profile", "avr1", "-b", "arduino:avr:nano", sketchWithDefProfilePath.String(), "--format", "json")
+		require.NoError(t, err)
+		jsonOut := requirejson.Parse(t, stdout)
+		jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`)
+		jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`)
+	}
+}
diff --git a/internal/integrationtest/testdata/sketch_with_default_profile/sketch.yaml b/internal/integrationtest/testdata/sketch_with_default_profile/sketch.yaml
new file mode 100644
index 00000000000..95d13d5754e
--- /dev/null
+++ b/internal/integrationtest/testdata/sketch_with_default_profile/sketch.yaml
@@ -0,0 +1,12 @@
+profiles:
+  avr1:
+    fqbn: arduino:avr:uno
+    platforms:
+      - platform: arduino:avr (1.8.4)
+
+  avr2:
+    fqbn: arduino:avr:leonardo
+    platforms:
+      - platform: arduino:avr (1.8.5)
+
+default_profile: avr2
diff --git a/internal/integrationtest/testdata/sketch_with_default_profile/sketch_with_default_profile.ino b/internal/integrationtest/testdata/sketch_with_default_profile/sketch_with_default_profile.ino
new file mode 100644
index 00000000000..660bdbccfdb
--- /dev/null
+++ b/internal/integrationtest/testdata/sketch_with_default_profile/sketch_with_default_profile.ino
@@ -0,0 +1,2 @@
+void setup() {}
+void loop() {}
diff --git a/internal/integrationtest/testdata/sketch_without_default_profile/sketch.yaml b/internal/integrationtest/testdata/sketch_without_default_profile/sketch.yaml
new file mode 100644
index 00000000000..1344a8fa507
--- /dev/null
+++ b/internal/integrationtest/testdata/sketch_without_default_profile/sketch.yaml
@@ -0,0 +1,10 @@
+profiles:
+  avr1:
+    fqbn: arduino:avr:uno
+    platforms:
+      - platform: arduino:avr (1.8.4)
+
+  avr2:
+    fqbn: arduino:avr:leonardo
+    platforms:
+      - platform: arduino:avr (1.8.5)
diff --git a/internal/integrationtest/testdata/sketch_without_default_profile/sketch_without_default_profile.ino b/internal/integrationtest/testdata/sketch_without_default_profile/sketch_without_default_profile.ino
new file mode 100644
index 00000000000..660bdbccfdb
--- /dev/null
+++ b/internal/integrationtest/testdata/sketch_without_default_profile/sketch_without_default_profile.ino
@@ -0,0 +1,2 @@
+void setup() {}
+void loop() {}