diff --git a/cli/options/options.go b/cli/options/options.go
index 09bb08572..adafb6166 100644
--- a/cli/options/options.go
+++ b/cli/options/options.go
@@ -73,6 +73,13 @@ var ConfigFile = cli.StringFlag{
 	Usage: "path to the node configuration file (overrides --config-path option)",
 }
 
+// RelativePath is a flag for commands that use node configuration and provide
+// a prefix to all relative paths in config files.
+var RelativePath = cli.StringFlag{
+	Name:  "relative-path",
+	Usage: "a prefix to all relative paths in the node configuration file",
+}
+
 // Debug is a flag for commands that allow node in debug mode usage.
 var Debug = cli.BoolFlag{
 	Name:  "debug, d",
@@ -159,15 +166,18 @@ func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transac
 // GetConfigFromContext looks at the path and the mode flags in the given config and
 // returns an appropriate config.
 func GetConfigFromContext(ctx *cli.Context) (config.Config, error) {
-	var configFile = ctx.String("config-file")
+	var (
+		configFile   = ctx.String("config-file")
+		relativePath = ctx.String("relative-path")
+	)
 	if len(configFile) != 0 {
-		return config.LoadFile(configFile)
+		return config.LoadFile(configFile, relativePath)
 	}
 	var configPath = "./config"
 	if argCp := ctx.String("config-path"); argCp != "" {
 		configPath = argCp
 	}
-	return config.Load(configPath, GetNetwork(ctx))
+	return config.Load(configPath, GetNetwork(ctx), relativePath)
 }
 
 var (
diff --git a/cli/server/server.go b/cli/server/server.go
index 7e705d433..f2670d599 100644
--- a/cli/server/server.go
+++ b/cli/server/server.go
@@ -34,7 +34,7 @@ import (
 
 // NewCommands returns 'node' command.
 func NewCommands() []cli.Command {
-	cfgFlags := []cli.Flag{options.Config, options.ConfigFile}
+	cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
 	cfgFlags = append(cfgFlags, options.Network...)
 	var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
 	copy(cfgWithCountFlags, cfgFlags)
diff --git a/cli/server/server_test.go b/cli/server/server_test.go
index 2757ae6bb..d19278d91 100644
--- a/cli/server/server_test.go
+++ b/cli/server/server_test.go
@@ -49,6 +49,18 @@ func TestGetConfigFromContext(t *testing.T) {
 		require.NoError(t, err)
 		require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
 	})
+	t.Run("relative-path", func(t *testing.T) {
+		set := flag.NewFlagSet("flagSet", flag.ExitOnError)
+		set.String("relative-path", "../../config", "")
+		set.Bool("testnet", true, "")
+		set.String("config-file", "../../config/protocol.testnet.yml", "")
+		ctx := cli.NewContext(cli.NewApp(), set, nil)
+		cfg, err := options.GetConfigFromContext(ctx)
+		require.NoError(t, err)
+		require.Equal(t, "../../config/chains/testnet", cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
+		require.Equal(t, "/cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path)
+		require.Equal(t, "/notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
+	})
 }
 
 func TestHandleLoggingParams(t *testing.T) {
diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go
index 8e7d22887..24bdbe7cc 100644
--- a/cli/vm/cli_test.go
+++ b/cli/vm/cli_test.go
@@ -95,8 +95,10 @@ func newTestVMCLIWithLogoAndCustomConfig(t *testing.T, printLogo bool, cfg *conf
 	if cfg == nil {
 		configPath := "../../config/protocol.unit_testnet.single.yml"
 		var err error
-		c, err = config.LoadFile(configPath)
+		c, err = config.LoadFile(configPath, "../../config")
 		require.NoError(t, err, "could not load chain config")
+		require.Equal(t, "../../testdata/wallet1_solo.json", c.ApplicationConfiguration.Consensus.UnlockWallet.Path)
+		require.Equal(t, "/notary_wallet.json", c.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
 		c.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
 	} else {
 		c = *cfg
diff --git a/cli/vm/vm.go b/cli/vm/vm.go
index 89dd351bb..e9e1e2a58 100644
--- a/cli/vm/vm.go
+++ b/cli/vm/vm.go
@@ -13,7 +13,7 @@ import (
 
 // NewCommands returns 'vm' command.
 func NewCommands() []cli.Command {
-	cfgFlags := []cli.Flag{options.Config, options.ConfigFile}
+	cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
 	cfgFlags = append(cfgFlags, options.Network...)
 	return []cli.Command{{
 		Name:   "vm",
diff --git a/pkg/config/config.go b/pkg/config/config.go
index d30a30e61..06b3d3754 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"os"
+	"path/filepath"
 	"time"
 
 	"github.com/nspcc-dev/neo-go/pkg/config/netmode"
@@ -57,15 +58,17 @@ func (c Config) Blockchain() Blockchain {
 }
 
 // Load attempts to load the config from the given
-// path for the given netMode.
-func Load(path string, netMode netmode.Magic) (Config, error) {
+// path for the given netMode. If relativePath is not empty, relative paths in the
+// config will be updated based on the provided relative path.
+func Load(path string, netMode netmode.Magic, relativePath ...string) (Config, error) {
 	configPath := fmt.Sprintf("%s/protocol.%s.yml", path, netMode)
-	return LoadFile(configPath)
+	return LoadFile(configPath, relativePath...)
 }
 
 // LoadFile loads config from the provided path. It also applies backwards compatibility
-// fixups if necessary.
-func LoadFile(configPath string) (Config, error) {
+// fixups if necessary. If relativePath is not empty, relative paths in the config will
+// be updated based on the provided relative path.
+func LoadFile(configPath string, relativePath ...string) (Config, error) {
 	if _, err := os.Stat(configPath); os.IsNotExist(err) {
 		return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath)
 	}
@@ -89,6 +92,9 @@ func LoadFile(configPath string) (Config, error) {
 	if err != nil {
 		return Config{}, fmt.Errorf("failed to unmarshal config YAML: %w", err)
 	}
+	if len(relativePath) == 1 && relativePath[0] != "" {
+		updateRelativePaths(relativePath[0], &config)
+	}
 
 	err = config.ProtocolConfiguration.Validate()
 	if err != nil {
@@ -97,3 +103,20 @@ func LoadFile(configPath string) (Config, error) {
 
 	return config, nil
 }
+
+// updateRelativePaths updates relative paths in the config structure based on the provided relative path.
+func updateRelativePaths(relativePath string, config *Config) {
+	updatePath := func(path *string) {
+		if *path != "" && !filepath.IsAbs(*path) {
+			*path = filepath.Join(relativePath, *path)
+		}
+	}
+
+	updatePath(&config.ApplicationConfiguration.LogPath)
+	updatePath(&config.ApplicationConfiguration.DBConfiguration.BoltDBOptions.FilePath)
+	updatePath(&config.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
+	updatePath(&config.ApplicationConfiguration.Consensus.UnlockWallet.Path)
+	updatePath(&config.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
+	updatePath(&config.ApplicationConfiguration.Oracle.UnlockWallet.Path)
+	updatePath(&config.ApplicationConfiguration.StateRoot.UnlockWallet.Path)
+}