diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go
index 50596c4e0..dffdf2a45 100644
--- a/shared/services/config/rocket-pool-config.go
+++ b/shared/services/config/rocket-pool-config.go
@@ -57,6 +57,9 @@ type RocketPoolConfig struct {
 
 	IsNativeMode bool `yaml:"-"`
 
+	Offline     config.Parameter `yaml:"offline"`
+	NodeAddress config.Parameter `yaml:"nodeAddress"`
+
 	// Execution client settings
 	ExecutionClientMode config.Parameter `yaml:"executionClientMode,omitempty"`
 	ExecutionClient     config.Parameter `yaml:"executionClient,omitempty"`
@@ -213,6 +216,30 @@ func NewRocketPoolConfig(rpDir string, isNativeMode bool) *RocketPoolConfig {
 			}},
 		},
 
+		Offline: config.Parameter{
+			ID:                   "offline",
+			Name:                 "Offline Mode",
+			Description:          "Enable this if you would like to keep your node operator key offline.",
+			Type:                 config.ParameterType_Bool,
+			Default:              map[config.Network]interface{}{config.Network_All: false},
+			AffectsContainers:    []config.ContainerID{config.ContainerID_Api, config.ContainerID_Node},
+			EnvironmentVariables: []string{},
+			CanBeBlank:           false,
+			OverwriteOnUpgrade:   false,
+		},
+
+		NodeAddress: config.Parameter{
+			ID:                   "nodeAddress",
+			Name:                 "Node Operator Address",
+			Description:          "The address of the node operator key. Only used in conjunction with offline mode.",
+			Type:                 config.ParameterType_String,
+			Default:              map[config.Network]interface{}{config.Network_All: ""},
+			AffectsContainers:    []config.ContainerID{config.ContainerID_Api, config.ContainerID_Node, config.ContainerID_Watchtower},
+			EnvironmentVariables: []string{},
+			CanBeBlank:           true,
+			OverwriteOnUpgrade:   false,
+		},
+
 		UseFallbackClients: config.Parameter{
 			ID:                   "useFallbackClients",
 			Name:                 "Use Fallback Clients",
@@ -527,6 +554,8 @@ func (cfg *RocketPoolConfig) GetParameters() []*config.Parameter {
 		&cfg.ReconnectDelay,
 		&cfg.ConsensusClientMode,
 		&cfg.ConsensusClient,
+		&cfg.Offline,
+		&cfg.NodeAddress,
 		&cfg.ExternalConsensusClient,
 		&cfg.EnableMetrics,
 		&cfg.EnableODaoMetrics,
diff --git a/shared/services/services.go b/shared/services/services.go
index c4cd82a0e..9abf39e92 100644
--- a/shared/services/services.go
+++ b/shared/services/services.go
@@ -216,6 +216,17 @@ func getWallet(c *cli.Context, cfg *config.RocketPoolConfig, pm *passwords.Passw
 			return
 		}
 
+		// Offline mode
+		if cfg.Offline.Value == true {
+			operatorAddress := cfg.NodeAddress.Value.(string)
+
+			if operatorAddress == "" {
+				fmt.Println("Offline mode enabled, but no operator address specified in config file, skipping offline setup...")
+				return
+			}
+			nodeWallet.SetOffline(operatorAddress)
+		}
+
 		// Keystores
 		lighthouseKeystore := lhkeystore.NewKeystore(os.ExpandEnv(cfg.Smartnode.GetValidatorKeychainPath()), pm)
 		lodestarKeystore := lokeystore.NewKeystore(os.ExpandEnv(cfg.Smartnode.GetValidatorKeychainPath()), pm)
diff --git a/shared/services/wallet/node.go b/shared/services/wallet/node.go
index 6619a835a..4fdd44e89 100644
--- a/shared/services/wallet/node.go
+++ b/shared/services/wallet/node.go
@@ -3,6 +3,7 @@ package wallet
 import (
 	"context"
 	"crypto/ecdsa"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"math/big"
@@ -10,6 +11,8 @@ import (
 	"github.com/btcsuite/btcd/btcutil/hdkeychain"
 	"github.com/ethereum/go-ethereum/accounts"
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
 )
 
@@ -21,6 +24,10 @@ func (w *Wallet) GetNodeAccount() (accounts.Account, error) {
 		return accounts.Account{}, errors.New("Wallet is not initialized")
 	}
 
+	if w.Offline() {
+		return *w.nodeAddress, nil
+	}
+
 	// Get private key
 	privateKey, path, err := w.getNodePrivateKey()
 	if err != nil {
@@ -53,6 +60,25 @@ func (w *Wallet) GetNodeAccountTransactor() (*bind.TransactOpts, error) {
 		return nil, errors.New("Wallet is not initialized")
 	}
 
+	if w.Offline() {
+		var transactor bind.TransactOpts
+		transactor.From = w.nodeAddress.Address
+		transactor.Context = context.Background()
+		transactor.GasFeeCap = w.maxFee
+		transactor.GasTipCap = w.maxPriorityFee
+		transactor.GasLimit = w.gasLimit
+		transactor.Signer = func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
+			txJSON, err := json.MarshalIndent(tx, "", "  ")
+			if err != nil {
+				return tx, err
+			}
+			fmt.Printf("Offline mode: this transaction would have been signed by %s:\n%s\n", address.String(), string(txJSON))
+			return nil, fmt.Errorf("Offline mode - transaction not signed")
+		}
+
+		return &transactor, nil
+	}
+
 	// Get private key
 	privateKey, _, err := w.getNodePrivateKey()
 	if err != nil {
diff --git a/shared/services/wallet/wallet.go b/shared/services/wallet/wallet.go
index 50dfd18f4..fd17df0b7 100644
--- a/shared/services/wallet/wallet.go
+++ b/shared/services/wallet/wallet.go
@@ -11,6 +11,7 @@ import (
 	"github.com/btcsuite/btcd/btcutil/hdkeychain"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/ethereum/go-ethereum/accounts"
+	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/google/uuid"
@@ -39,6 +40,7 @@ type Wallet struct {
 	pm         *passwords.PasswordManager
 	encryptor  *eth2ks.Encryptor
 	chainID    *big.Int
+	offline    bool
 
 	// Encrypted store
 	ws *walletStore
@@ -48,6 +50,7 @@ type Wallet struct {
 	mk   *hdkeychain.ExtendedKey
 
 	// Node key cache
+	nodeAddress *accounts.Account
 	nodeKey     *ecdsa.PrivateKey
 	nodeKeyPath string
 
@@ -100,6 +103,18 @@ func NewWallet(walletPath string, chainId uint, maxFee *big.Int, maxPriorityFee
 
 }
 
+func (w *Wallet) Offline() bool {
+	return w.offline
+}
+
+// This function designates a wallet as offline
+func (w *Wallet) SetOffline(address string) {
+	w.offline = true
+	w.nodeAddress = &accounts.Account{
+		Address: common.HexToAddress(address),
+	}
+}
+
 // Gets the wallet's chain ID
 func (w *Wallet) GetChainID() *big.Int {
 	copy := big.NewInt(0).Set(w.chainID)
@@ -113,7 +128,7 @@ func (w *Wallet) AddKeystore(name string, ks keystore.Keystore) {
 
 // Check if the wallet has been initialized
 func (w *Wallet) IsInitialized() bool {
-	return (w.ws != nil && w.seed != nil && w.mk != nil)
+	return w.offline || (w.ws != nil && w.seed != nil && w.mk != nil)
 }
 
 // Attempt to initialize the wallet if not initialized and return status