The site is under maintenance, so logging in is temporarily unavailable. All resources and the MCP still work.

AI Skills

Where Roblox scripts run

By Roblox Vibecoders · roblox-script-run-context

Use when creating, moving or debugging Roblox scripts - choosing between Script (RunContext Legacy, Server or Client), LocalScript and ModuleScript, deciding where each goes (ServerScriptService, ServerStorage, ReplicatedStorage, ReplicatedFirst, StarterPlayerScripts, StarterCharacterScripts, StarterGui, Workspace), or when a script doesn't run, runs twice, or can't find a module. Also covers Actors and parallel Luau briefly.

Install it

Terminal
mkdir -p ~/.claude/skills/roblox-script-run-context && curl -fsSL https://robloxvibecoders.com/skills/roblox-script-run-context/SKILL.md -o ~/.claude/skills/roblox-script-run-context/SKILL.md

Skills in ~/.claude/skills work in every project. Put them in your project's .claude/skills folder to share them with your team.

What your AI reads

SKILL.md
---
name: roblox-script-run-context
description: Use when creating, moving or debugging Roblox scripts - choosing between Script (RunContext Legacy, Server or Client), LocalScript and ModuleScript, deciding where each goes (ServerScriptService, ServerStorage, ReplicatedStorage, ReplicatedFirst, StarterPlayerScripts, StarterCharacterScripts, StarterGui, Workspace), or when a script doesn't run, runs twice, or can't find a module. Also covers Actors and parallel Luau briefly.
---

# Where Roblox scripts run

Whether code runs at all, and on which machine, depends on three things: the script type, its `RunContext`, and its
location. Most "my script does nothing" and "it runs twice" bugs come from getting one of them wrong.

## The three script types

- **Script**: has a `RunContext` property.
  - `Legacy` (the default): server only, and only in server containers such as `ServerScriptService` and `Workspace`.
  - `Server`: server only, runs anywhere in the place, including `ServerStorage` and `ReplicatedStorage`.
  - `Client`: client only, runs anywhere the client has it, including `ReplicatedStorage` and `ReplicatedFirst`.
  - `Plugin` exists in the enum; don't use it in game code.
- **LocalScript**: client only, no RunContext. Runs only as a descendant of the player's `Backpack`, `PlayerGui`,
  `PlayerScripts`, the local player's character, or `ReplicatedFirst`. It does **not** run in `ReplicatedStorage`, or
  anywhere in `Workspace` outside the local player's character.
- **ModuleScript**: no RunContext. Runs when something calls `require` on it, in that caller's environment.

## Locations

| Location | What goes there | Notes |
|---|---|---|
| `ServerScriptService` | the server entry Script, server modules | never replicated to clients |
| `ServerStorage` | server modules and assets | `Legacy` Scripts don't run here, `Server` Scripts do |
| `ReplicatedStorage` | shared modules, the client entry Script (`RunContext = Client`) | everything here is readable by clients |
| `ReplicatedFirst` | loading screen only | replicates before anything else |
| `StarterPlayerScripts` | LocalScripts that run once per join | copied into `Player.PlayerScripts` |
| `StarterCharacterScripts` | LocalScripts (or Legacy Scripts) per character | copied into the character on every spawn |
| `StarterGui` | ScreenGuis, UI LocalScripts | copied into `PlayerGui`; GUIs with `ResetOnSpawn = true` are recreated on respawn |
| `StarterPack` | Tools | copied into `Backpack` |
| `Workspace` | the world | avoid scripts inside parts; tag parts and handle them from one module |

**Starter containers copy their contents.** A `Server` or `Client` Script placed in one runs twice: the original and
the copy. Only LocalScripts and Legacy Scripts belong in `Starter*` containers.

## Recommended structure

Roblox's own recommendation: one server entry, one client entry, everything else in ModuleScripts.

```
ServerScriptService/
  Main            Script, RunContext = Server
  Services/       ModuleScripts (server only)
ReplicatedStorage/
  Client          Script, RunContext = Client  (or a LocalScript in StarterPlayerScripts)
  Controllers/    ModuleScripts (client)
  Shared/         ModuleScripts (both)
```

```luau
-- ServerScriptService/Main (Script, RunContext = Server)
local ServerScriptService = game:GetService("ServerScriptService")

local modules = {}
for _, module in ServerScriptService.Services:GetChildren() do
	if module:IsA("ModuleScript") then
		table.insert(modules, require(module))
	end
end

-- init everything first, then start, so modules can safely use each other in start()
for _, module in modules do
	if module.init then
		module.init()
	end
end
for _, module in modules do
	if module.start then
		task.spawn(module.start)
	end
end
```

With Rojo, `*.server.luau` becomes a Script and `*.client.luau` a LocalScript by default. Set
`"emitLegacyScripts": false` in the project file to get Scripts with `RunContext` Server/Client instead.

## ModuleScript rules

- A module runs **once per Luau environment** and every later `require` returns the same cached value. The server,
  each client, and the Studio Command Bar each have their own copy. State in a module is never shared between server
  and client.
- `require` yields only if the module's top level yields. Don't `WaitForChild` in loops or wait for data at the top
  level: every script that requires it waits too. Do that work in `start()`.
- A require cycle (A requires B, B requires A) never finishes; the docs say the thread hangs. Break the cycle by
  passing references in `init` or requiring lazily inside a function.
- A client can only require what it can see. Modules in `ServerScriptService`/`ServerStorage` don't exist on clients.
- Anything in `ReplicatedStorage` or `ReplicatedFirst` can be read by exploiters. Keep server logic, secrets and
  loot tables in server containers.

## Actors and parallel Luau (briefly)

Parallel Luau is for heavy, independent work (many NPCs, raycasts, procedural generation). Scripts must be
descendants of an `Actor`. Scripts under the same Actor run one after another, so split work across several Actors.

```luau
-- Script inside an Actor
local actor = script:GetActor()

actor:BindToMessageParallel("CheckLine", function(origin: Vector3, direction: Vector3)
	local result = workspace:Raycast(origin, direction) -- runs in parallel
	task.synchronize() -- back to serial before touching Instances or shared state
	if result then
		result.Instance:SetAttribute("Seen", true)
	end
end)
```

```luau
-- Elsewhere (serial)
workerActor:SendMessage("CheckLine", Vector3.new(0, 10, 0), Vector3.new(0, -50, 0))
```

- `task.desynchronize()` switches the current thread to parallel; `task.synchronize()` switches back.
  `signal:ConnectParallel(fn)` runs a handler in parallel directly.
- You can't `require()` in a parallel phase: require modules first, in serial.
- Most API members can't be written (or called) in parallel; a member without a thread-safety tag is Unsafe. Change
  Instances after `task.synchronize()`.
- Share data between Actors with `actor:SendMessage` / `BindToMessage` / `BindToMessageParallel` or a `SharedTable`,
  not with a module's table.

## Mistakes to avoid

- A LocalScript in `ReplicatedStorage` or in a Workspace model (it never runs). Use a `Client` Script there instead.
- A `Server` or `Client` Script in `StarterPlayerScripts`, `StarterCharacterScripts` or `StarterGui` (runs twice).
- Moving a `Server` Script into `ServerStorage` to "turn it off". It still runs. Set `Enabled = false`.
- Server Scripts or server-only modules in `ReplicatedStorage`: they work, but every client can read the source.
- Per-character LocalScripts that set things up once: they rerun on every respawn. Put once-per-session code in
  `StarterPlayerScripts` and handle `CharacterAdded` there.
- UI that "resets" on death: the ScreenGui has `ResetOnSpawn = true`.
- One Script per part in Workspace. Tag the parts and drive them from one module with CollectionService.
- Expecting a value set by the server in a shared module to appear on the client. Replicate it (attributes, remotes).