# Godot Project Settings System: How Configuration Values Are Stored and Retrieved

> Explore the Godot project settings system. Learn how configuration values are stored in thread-safe singletons and saved to project.binary or project.godot files with versioning and change tracking.

- Repository: [Godot Engine/godot](https://github.com/godotengine/godot)
- Tags: internals
- Published: 2026-02-26

---

**The Godot project settings system is a thread-safe singleton that stores configuration values in an ordered map of `VariantContainer` structs, persisting them to either `project.binary` or `project.godot` files with automatic versioning and change tracking.**

The project settings system in godotengine/godot serves as the central configuration manager for both the editor and exported games. Located in `core/config/`, this thread-safe singleton handles everything from rendering quality to input mappings, storing values in specialized containers that track defaults, modifications, and persistence flags.

## Core Architecture of the Project Settings System

### The Singleton Pattern and Global Access

At the heart of the system is the `ProjectSettings` singleton, accessible via `ProjectSettings::get_singleton()` defined in [[`core/config/project_settings.h`](https://github.com/godotengine/godot/blob/main/core/config/project_settings.h) at line 53](https://github.com/godotengine/godot/blob/master/core/config/project_settings.h#L53). This global entry point ensures that both engine core systems and GDScript code interact with the same configuration state throughout the application lifecycle.

### The VariantContainer Storage Structure

All settings reside in an `RBMap<StringName, VariantContainer> props` container, where each key follows the `"section/property"` format (e.g., `"rendering/quality/filters"`). The [`VariantContainer` struct](https://github.com/godotengine/godot/blob/master/core/config/project_settings.h#L74) defined at lines 74-82 stores:

- `variant`: The current runtime value as a `Variant`
- `initial`: The default value assigned when the setting was first registered
- Ordering metadata for editor presentation
- Meta-flags including `persist`, `basic`, `internal`, and `restart_if_changed`

### Change Tracking and Metadata

Beyond raw values, the system maintains auxiliary collections to manage state:

- `changed_settings`: Tracks which keys were modified during the current frame
- `custom_prop_info`: Holds `PropertyInfo` for editor tooling and validation
- `feature_overrides`: Contains platform-specific overrides consulted by `get_setting_with_override()`

These structures enable the editor to highlight modified values and prompt for application restarts when critical graphics or window settings change.

## How Configuration Values Are Stored and Loaded

### File Format Hierarchy and Parsing

When Godot loads a project, it attempts to read the binary format first through [`_load_settings_binary()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L511) (lines 511-534), looking for `project.binary` optimized for speed. If unavailable or unreadable, it falls back to [`_load_settings_text()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L560) (lines 560-585) to parse the human-readable `project.godot` file. Both parsers populate the `props` map via `set()`, which internally delegates to `_set()`.

### The Storage Lifecycle and Persistence

When storing a value through `set()` or `set_setting()`, the engine checks if the key exists and whether the value differs from the current `variant`. If modified, [`_set()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L318) (lines 318-339) updates the `VariantContainer`, increments the global `_version` counter, and records the change. During save operations, [`save_custom()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L1232) (lines 1232-1250) writes only properties where `variant != initial` (unless forcing a full dump), ordered by `VariantContainer::order` to maintain editor-friendly organization.

### Versioning and Migration Strategy

The `CONFIG_VERSION` constant (currently `5` defined in [[`project_settings.h`](https://github.com/godotengine/godot/blob/main/project_settings.h) at line 60](https://github.com/godotengine/godot/blob/master/core/config/project_settings.h#L60)) guards compatibility between engine versions. When loading older files, [`_convert_to_last_version()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L622) (lines 622-644) automatically migrates data to the current format, ensuring projects remain compatible across engine updates.

## Working with Project Settings in Code

### GDScript Usage

```gdscript

# Access the singleton (GDScript automatically binds to the C++ class)

var settings = ProjectSettings.get_singleton()

# Read a configuration value with a fallback default

var fps_limit = settings.get_setting("application/run/fps_limit", 60)

# Modify a setting at runtime (e.g., enable v-sync)

settings.set_setting("display/window/vsync_mode", DisplayServer.VSYNC_ENABLED)

# Mark a setting as requiring restart when altered

ProjectSettings.set_restart_if_changed("rendering/renderer/quality", true)

# Check if any settings under a prefix changed this frame

if ProjectSettings.check_changed_settings_in_group("display/window/"):
    print("Window settings were modified")

# Persist changes to disk

var err = ProjectSettings.save()
if err == OK:
    print("Project saved successfully")

```

### C++ Implementation

```cpp
// Retrieve a value with default fallback (from project_settings.cpp)
Variant ProjectSettings::get_setting(const String &p_setting, const Variant &p_default) const {
    if (has_setting(p_setting))
        return get(p_setting);
    else
        return p_default;
}

// Store a value and trigger change tracking
void ProjectSettings::set_setting(const String &p_setting, const Variant &p_value) {
    set(p_setting, p_value); // delegates to _set() internally
}

```

Both GDScript and C++ APIs ultimately invoke the internal `_set()` and `_get()` functions that manipulate the `RBMap` storage directly, as implemented in [[`core/config/project_settings.cpp`](https://github.com/godotengine/godot/blob/main/core/config/project_settings.cpp)](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp).

## Summary

- The project settings system uses a **thread-safe singleton** pattern accessible globally via `ProjectSettings::get_singleton()` defined in [`core/config/project_settings.h`](https://github.com/godotengine/godot/blob/main/core/config/project_settings.h)
- Values are stored in **`RBMap<StringName, VariantContainer>`**, where each entry tracks both current and initial values alongside persistence and restart flags
- Godot prioritizes **`project.binary`** for loading speed, falling back to the text-based **`project.godot`** when necessary
- Only modified properties (where `variant != initial`) are written during save operations, ordered by the `order` metadata to maintain editor-friendly organization
- **Configuration version 5** includes automatic migration logic via `_convert_to_last_version()` for backward compatibility across engine updates

## Frequently Asked Questions

### Where does Godot store project settings on disk?

Godot stores project settings in either `project.binary` (a fast, versioned binary format) or `project.godot` (a human-readable text file) in the project root. According to the source code in [[`core/config/project_settings.cpp`](https://github.com/godotengine/godot/blob/main/core/config/project_settings.cpp)](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L511), the engine attempts to load the binary version first for performance, falling back to the text file if the binary is missing or corrupted.

### What is the difference between project.godot and project.binary?

The `project.godot` file uses an INI-style text format readable by humans and version control systems, while `project.binary` is an optimized binary representation that parses faster. As implemented in [`_load_settings_binary()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L511) and [`_load_settings_text()`](https://github.com/godotengine/godot/blob/master/core/config/project_settings.cpp#L560), both formats contain identical data, but the binary version includes a `config_version` header for automated migration checking.

### How do I check if a setting has changed during runtime?

Use `ProjectSettings.check_changed_settings_in_group()` to query if any keys within a specific prefix (such as `"display/window/"`) were modified during the current frame. Internally, this checks against the `changed_settings` set populated whenever `_set()` detects a value modification, as defined in the public members section of [[`project_settings.h`](https://github.com/godotengine/godot/blob/main/project_settings.h)](https://github.com/godotengine/godot/blob/master/core/config/project_settings.h#L50).

### What happens when I modify a setting that requires a restart?

When a setting has the `restart_if_changed` flag enabled (configured via `set_restart_if_changed()`), the engine records this metadata in the `VariantContainer` struct. While the value updates immediately in the `props` map, the editor uses this flag to display restart notifications, and the change only takes full effect after the application restarts and reloads the configuration from disk.