Quick answer: Godot writes logs to ~/.local/share/godot/app_userdata/ProjectName/logs/ by default. Use OS.get_user_data_dir() from GDScript to find the path at runtime, ship a shell wrapper that redirects stderr and sets ulimit -c unlimited for core dumps, and auto-attach the previous session’s log to any bug reports submitted in the current session if the previous session ended without a clean-shutdown marker.
Linux players are some of your most valuable bug reporters — they’re often technical, detail-oriented, and willing to paste stack traces. But they’re also dealing with a much more varied environment than Windows or macOS players, which means log collection is harder and the bugs are frequently environmental. Here’s how to make sure you get the log files you need when a Godot game crashes on Linux.
Where Godot Writes Logs on Linux
Godot follows the XDG Base Directory specification on Linux. By default, it writes user data to $XDG_DATA_HOME/godot/app_userdata/ProjectName/, which expands to ~/.local/share/godot/app_userdata/ProjectName/ when XDG_DATA_HOME is unset.
The log file is at <user_data_dir>/logs/godot.log. Godot rotates logs automatically — after the configured number of runs (default 5), older logs are deleted. You can configure this in project settings under debug/file_logging.
# GDScript: find log location at runtime
func _ready():
var user_data = OS.get_user_data_dir()
print("User data dir: ", user_data)
print("Log file: ", user_data.path_join("logs").path_join("godot.log"))
# Check if logging is enabled
if ProjectSettings.get_setting("debug/file_logging/enable_file_logging"):
print("File logging is enabled")
else:
push_warning("File logging is DISABLED - enable it in project settings")
Make sure file logging is enabled for your release builds. Open Project Settings, find debug/file_logging/enable_file_logging, and toggle it on. Also set debug/file_logging/log_path if you want a custom location (which is useful if you want logs to persist across reinstalls).
The Steam Proton Wrinkle
If your game ships on Steam and some Linux players use Proton to run the Windows build (common for players who don’t trust native Linux builds of Godot games), the log paths are completely different. Proton creates a virtual Windows environment under ~/.local/share/Steam/steamapps/compatdata/<appid>/pfx/drive_c/users/steamuser/. Your game will write logs there, following Windows conventions.
The practical implication: don’t hardcode the Linux path in your support docs. Use OS.get_user_data_dir() at runtime, print it to a visible location (the main menu version panel, or the bug report dialog), and tell players to include that path’s contents in their report.
Ship a Shell Wrapper Script
The cleanest way to capture Linux crashes is to not launch your game binary directly. Ship a wrapper shell script that sets up the environment, captures stderr, and enables core dumps. Players run ./MyGame.sh instead of ./MyGame.
#!/usr/bin/env bash
# MyGame.sh - launcher wrapper
set -eu
# Resolve the script's own directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Prepare log directory
LOG_DIR="$HOME/.local/share/MyGame/logs"
mkdir -p "$LOG_DIR"
# Timestamp for this session
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
STDERR_LOG="$LOG_DIR/stderr_$TIMESTAMP.log"
STDOUT_LOG="$LOG_DIR/stdout_$TIMESTAMP.log"
# Enable core dumps for this session (session-local, not persistent)
ulimit -c unlimited
# Write a "session started" marker that the game will clear on clean shutdown
touch "$LOG_DIR/.session_active"
echo "$TIMESTAMP" > "$LOG_DIR/.current_session"
# Collect environment info that's useful for bug reports
{
echo "=== Environment ==="
echo "Date: $(date -u)"
echo "Kernel: $(uname -a)"
echo "Distro: $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME)"
echo "Desktop: ${XDG_CURRENT_DESKTOP:-unknown}"
echo "Session Type: ${XDG_SESSION_TYPE:-unknown}"
echo "CPU: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2-)"
echo "Memory: $(free -h | grep Mem:)"
echo "GPU: $(lspci | grep -i vga)"
echo ""
} > "$LOG_DIR/env_$TIMESTAMP.log"
# Launch the actual game, capturing stdout and stderr
./MyGame "$@" > "$STDOUT_LOG" 2> "$STDERR_LOG"
EXIT_CODE=$?
# If the game exited cleanly, remove the session marker
if [ $EXIT_CODE -eq 0 ]; then
rm -f "$LOG_DIR/.session_active"
fi
exit $EXIT_CODE
The .session_active marker is the key trick. When the game starts, you check whether this file exists from a previous run. If it does, the previous session didn’t exit cleanly — it crashed. That’s your signal to read the previous stderr log and attach it to the next bug report the player submits.
Detecting Previous Crashes From Within the Game
# crash_detector.gd - autoload singleton
extends Node
const SESSION_MARKER := "user://../../logs/.session_active"
var previous_crash_detected := false
var previous_stderr_log := ""
func _ready():
var marker_path = OS.get_user_data_dir().path_join("logs").path_join(".session_active")
if FileAccess.file_exists(marker_path):
previous_crash_detected = true
_load_previous_stderr()
# Mark this session as active
_write_session_marker()
func _load_previous_stderr():
var log_dir = OS.get_user_data_dir().path_join("logs")
var dir = DirAccess.open(log_dir)
if dir == null:
return
# Find the newest stderr_*.log file
var newest_file := ""
var newest_time := 0
dir.list_dir_begin()
var file = dir.get_next()
while file != "":
if file.begins_with("stderr_") and file.ends_with(".log"):
var full = log_dir.path_join(file)
var mtime = FileAccess.get_modified_time(full)
if mtime > newest_time:
newest_time = mtime
newest_file = full
file = dir.get_next()
if newest_file != "":
var f = FileAccess.open(newest_file, FileAccess.READ)
if f:
previous_stderr_log = f.get_as_text()
f.close()
func _exit_tree():
# Clean shutdown - clear the marker
var marker_path = OS.get_user_data_dir().path_join("logs").path_join(".session_active")
if FileAccess.file_exists(marker_path):
DirAccess.remove_absolute(marker_path)
On the next launch after a crash, you can show a non-intrusive notification: “Looks like the game crashed last time. Would you like to send a report?” Players are much more likely to submit a report in that moment than hours later.
Capturing Core Dumps With systemd-coredump
On modern distros, core dumps are handled by systemd-coredump. When your game crashes, a full memory dump is saved to /var/lib/systemd/coredump/. You can extract it with coredumpctl list and coredumpctl dump PID.
For automated collection, add instructions to your bug report dialog that tell Linux players how to retrieve the core dump:
# In your in-game bug report help text for Linux
coredumpctl list MyGame
coredumpctl dump MyGame > mygame.core
# Then attach mygame.core to your bug report
Core dumps can be hundreds of MB, so don’t auto-upload them — ask the player first and offer an opt-in. For the overwhelming majority of crashes, the stderr log is sufficient and the core dump isn’t needed.
Related Issues
For crash dump collection across platforms, see Capture and Symbolicate Crash Dumps From Player Devices. For Godot stack trace debugging specifically, check Godot Stack Trace Debugging Guide. For general Linux game debugging, see How to Debug Game Crashes on Player Devices.
Linux players will give you perfect reproduction steps if you give them a way to attach the logs.