diff --git a/cmd/joj3/conf/conf.go b/cmd/joj3/conf/conf.go
index 944e62c..d14ae1c 100644
--- a/cmd/joj3/conf/conf.go
+++ b/cmd/joj3/conf/conf.go
@@ -14,16 +14,21 @@ import (
 )
 
 type Conf struct {
-	SandboxExecServer string `default:"localhost:5051"`
-	SandboxToken      string `default:""`
-	LogPath           string `default:""`
-	OutputPath        string `default:"joj3_result.json"`
-	GradingRepoName   string `default:""`
-	SkipTeapot        bool   `default:"true"`
-	ScoreboardPath    string `default:"scoreboard.csv"`
-	FailedTablePath   string `default:"failed-table.md"`
-	Name              string `default:"unknown"`
-	Stages            []struct {
+	Name    string `default:"unknown"`
+	LogPath string `default:""`
+	Stage   struct {
+		SandboxExecServer string `default:"localhost:5051"`
+		SandboxToken      string `default:""`
+		OutputPath        string `default:"stages_result.json"`
+	}
+	Teapot struct {
+		Skip            bool   `default:"true"`
+		LogPath         string `default:"/home/tt/.cache/joint-teapot-debug.log"`
+		ScoreboardPath  string `default:"scoreboard.csv"`
+		FailedTablePath string `default:"failed-table.md"`
+		GradingRepoName string `default:""`
+	}
+	Stages []struct {
 		Name     string
 		Group    string
 		Executor struct {
diff --git a/cmd/joj3/stage/main.go b/cmd/joj3/stage/main.go
index 8d88791..51a857e 100644
--- a/cmd/joj3/stage/main.go
+++ b/cmd/joj3/stage/main.go
@@ -74,7 +74,10 @@ func outputResult(outputPath string, results []stage.StageResult) error {
 }
 
 func Run(conf conf.Conf, group string) error {
-	executors.InitWithConf(conf.SandboxExecServer, conf.SandboxToken)
+	executors.InitWithConf(
+		conf.Stage.SandboxExecServer,
+		conf.Stage.SandboxToken,
+	)
 	stages, err := generateStages(conf, group)
 	if err != nil {
 		slog.Error("generate stages", "error", err)
@@ -86,7 +89,7 @@ func Run(conf conf.Conf, group string) error {
 		slog.Error("run stages", "error", err)
 		return err
 	}
-	if err := outputResult(conf.OutputPath, results); err != nil {
+	if err := outputResult(conf.Stage.OutputPath, results); err != nil {
 		slog.Error("output result", "error", err)
 		return err
 	}
diff --git a/cmd/joj3/teapot/main.go b/cmd/joj3/teapot/main.go
index 74a4a60..a88d872 100644
--- a/cmd/joj3/teapot/main.go
+++ b/cmd/joj3/teapot/main.go
@@ -12,10 +12,10 @@ import (
 )
 
 func Run(conf conf.Conf) error {
-	if conf.SkipTeapot {
+	if conf.Teapot.Skip {
 		return nil
 	}
-	os.Setenv("LOG_FILE_PATH", "/home/tt/.cache/joint-teapot-debug.log")
+	os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath)
 	os.Setenv("_TYPER_STANDARD_TRACEBACK", "1")
 	envFilePath := "/home/tt/.config/teapot/teapot.env"
 	actor := os.Getenv("GITHUB_ACTOR")
@@ -30,8 +30,8 @@ func Run(conf conf.Conf) error {
 	repoName := repoParts[1]
 	re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
 	cmd := exec.Command("joint-teapot", "joj3-scoreboard",
-		envFilePath, conf.OutputPath, actor, conf.GradingRepoName, repoName,
-		runNumber, conf.ScoreboardPath, conf.Name) // #nosec G204
+		envFilePath, conf.Stage.OutputPath, actor, conf.Teapot.GradingRepoName,
+		repoName, runNumber, conf.Teapot.ScoreboardPath, conf.Name) // #nosec G204
 	outputBytes, err := cmd.CombinedOutput()
 	output := re.ReplaceAllString(string(outputBytes), "")
 	for _, line := range strings.Split(output, "\n") {
@@ -45,8 +45,8 @@ func Run(conf conf.Conf) error {
 		return err
 	}
 	cmd = exec.Command("joint-teapot", "joj3-failed-table",
-		envFilePath, conf.OutputPath, actor, conf.GradingRepoName, repoName,
-		runNumber, conf.FailedTablePath, conf.Name) // #nosec G204
+		envFilePath, conf.Stage.OutputPath, actor, conf.Teapot.GradingRepoName,
+		repoName, runNumber, conf.Teapot.FailedTablePath, conf.Name) // #nosec G204
 	outputBytes, err = cmd.CombinedOutput()
 	output = re.ReplaceAllString(string(outputBytes), "")
 	for _, line := range strings.Split(output, "\n") {
@@ -60,7 +60,7 @@ func Run(conf conf.Conf) error {
 		return err
 	}
 	cmd = exec.Command("joint-teapot", "joj3-create-result-issue",
-		envFilePath, conf.OutputPath, repoName, runNumber, conf.Name) // #nosec G204
+		envFilePath, conf.Stage.OutputPath, repoName, runNumber, conf.Name) // #nosec G204
 	outputBytes, err = cmd.CombinedOutput()
 	output = re.ReplaceAllString(string(outputBytes), "")
 	for _, line := range strings.Split(output, "\n") {